162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2016-2018 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bitops.h> 562306a36Sopenharmony_ci#include <linux/errno.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/string.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "nfp_asm.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciconst struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { 1362306a36Sopenharmony_ci [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 }, 1462306a36Sopenharmony_ci [CMD_TGT_WRITE32_SWAP] = { 0x02, 0x5f }, 1562306a36Sopenharmony_ci [CMD_TGT_READ8] = { 0x01, 0x43 }, 1662306a36Sopenharmony_ci [CMD_TGT_READ32] = { 0x00, 0x5c }, 1762306a36Sopenharmony_ci [CMD_TGT_READ32_LE] = { 0x01, 0x5c }, 1862306a36Sopenharmony_ci [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c }, 1962306a36Sopenharmony_ci [CMD_TGT_READ_LE] = { 0x01, 0x40 }, 2062306a36Sopenharmony_ci [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, 2162306a36Sopenharmony_ci [CMD_TGT_ADD] = { 0x00, 0x47 }, 2262306a36Sopenharmony_ci [CMD_TGT_ADD_IMM] = { 0x02, 0x47 }, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic bool unreg_is_imm(u16 reg) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci return (reg & UR_REG_IMM) == UR_REG_IMM; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciu16 br_get_offset(u64 instr) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u16 addr_lo, addr_hi; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr); 3562306a36Sopenharmony_ci addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) | 3862306a36Sopenharmony_ci addr_lo; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_civoid br_set_offset(u64 *instr, u16 offset) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci u16 addr_lo, addr_hi; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); 4662306a36Sopenharmony_ci addr_hi = offset != addr_lo; 4762306a36Sopenharmony_ci *instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO); 4862306a36Sopenharmony_ci *instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi); 4962306a36Sopenharmony_ci *instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid br_add_offset(u64 *instr, u16 offset) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci u16 addr; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci addr = br_get_offset(*instr); 5762306a36Sopenharmony_ci br_set_offset(instr, addr + offset); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic bool immed_can_modify(u64 instr) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (FIELD_GET(OP_IMMED_INV, instr) || 6362306a36Sopenharmony_ci FIELD_GET(OP_IMMED_SHIFT, instr) || 6462306a36Sopenharmony_ci FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) { 6562306a36Sopenharmony_ci pr_err("Can't decode/encode immed!\n"); 6662306a36Sopenharmony_ci return false; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci return true; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciu16 immed_get_value(u64 instr) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u16 reg; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!immed_can_modify(instr)) 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci reg = FIELD_GET(OP_IMMED_A_SRC, instr); 7962306a36Sopenharmony_ci if (!unreg_is_imm(reg)) 8062306a36Sopenharmony_ci reg = FIELD_GET(OP_IMMED_B_SRC, instr); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_civoid immed_set_value(u64 *instr, u16 immed) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci if (!immed_can_modify(*instr)) 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) { 9162306a36Sopenharmony_ci *instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff); 9262306a36Sopenharmony_ci *instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff); 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci *instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff); 9562306a36Sopenharmony_ci *instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *instr &= ~OP_IMMED_IMM; 9962306a36Sopenharmony_ci *instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid immed_add_value(u64 *instr, u16 offset) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u16 val; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!immed_can_modify(*instr)) 10762306a36Sopenharmony_ci return; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci val = immed_get_value(*instr); 11062306a36Sopenharmony_ci immed_set_value(instr, val + offset); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u16 nfp_swreg_to_unreg(swreg reg, bool is_dst) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci bool lm_id, lm_dec = false; 11662306a36Sopenharmony_ci u16 val = swreg_value(reg); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci switch (swreg_type(reg)) { 11962306a36Sopenharmony_ci case NN_REG_GPR_A: 12062306a36Sopenharmony_ci case NN_REG_GPR_B: 12162306a36Sopenharmony_ci case NN_REG_GPR_BOTH: 12262306a36Sopenharmony_ci return val; 12362306a36Sopenharmony_ci case NN_REG_NNR: 12462306a36Sopenharmony_ci return UR_REG_NN | val; 12562306a36Sopenharmony_ci case NN_REG_XFER: 12662306a36Sopenharmony_ci return UR_REG_XFR | val; 12762306a36Sopenharmony_ci case NN_REG_LMEM: 12862306a36Sopenharmony_ci lm_id = swreg_lm_idx(reg); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci switch (swreg_lm_mode(reg)) { 13162306a36Sopenharmony_ci case NN_LM_MOD_NONE: 13262306a36Sopenharmony_ci if (val & ~UR_REG_LM_IDX_MAX) { 13362306a36Sopenharmony_ci pr_err("LM offset too large\n"); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) | 13762306a36Sopenharmony_ci val; 13862306a36Sopenharmony_ci case NN_LM_MOD_DEC: 13962306a36Sopenharmony_ci lm_dec = true; 14062306a36Sopenharmony_ci fallthrough; 14162306a36Sopenharmony_ci case NN_LM_MOD_INC: 14262306a36Sopenharmony_ci if (val) { 14362306a36Sopenharmony_ci pr_err("LM offset in inc/dev mode\n"); 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci return UR_REG_LM | UR_REG_LM_POST_MOD | 14762306a36Sopenharmony_ci FIELD_PREP(UR_REG_LM_IDX, lm_id) | 14862306a36Sopenharmony_ci FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec); 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci pr_err("bad LM mode for unrestricted operands %d\n", 15162306a36Sopenharmony_ci swreg_lm_mode(reg)); 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci case NN_REG_IMM: 15562306a36Sopenharmony_ci if (val & ~0xff) { 15662306a36Sopenharmony_ci pr_err("immediate too large\n"); 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci return UR_REG_IMM_encode(val); 16062306a36Sopenharmony_ci case NN_REG_NONE: 16162306a36Sopenharmony_ci return is_dst ? UR_REG_NO_DST : REG_NONE; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci pr_err("unrecognized reg encoding %08x\n", reg); 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg, 16962306a36Sopenharmony_ci struct nfp_insn_ur_regs *reg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci memset(reg, 0, sizeof(*reg)); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Decode destination */ 17462306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_IMM) 17562306a36Sopenharmony_ci return -EFAULT; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_GPR_B) 17862306a36Sopenharmony_ci reg->dst_ab = ALU_DST_B; 17962306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_GPR_BOTH) 18062306a36Sopenharmony_ci reg->wr_both = true; 18162306a36Sopenharmony_ci reg->dst = nfp_swreg_to_unreg(dst, true); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Decode source operands */ 18462306a36Sopenharmony_ci if (swreg_type(lreg) == swreg_type(rreg) && 18562306a36Sopenharmony_ci swreg_type(lreg) != NN_REG_NONE) 18662306a36Sopenharmony_ci return -EFAULT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (swreg_type(lreg) == NN_REG_GPR_B || 18962306a36Sopenharmony_ci swreg_type(rreg) == NN_REG_GPR_A) { 19062306a36Sopenharmony_ci reg->areg = nfp_swreg_to_unreg(rreg, false); 19162306a36Sopenharmony_ci reg->breg = nfp_swreg_to_unreg(lreg, false); 19262306a36Sopenharmony_ci reg->swap = true; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci reg->areg = nfp_swreg_to_unreg(lreg, false); 19562306a36Sopenharmony_ci reg->breg = nfp_swreg_to_unreg(rreg, false); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci reg->dst_lmextn = swreg_lmextn(dst); 19962306a36Sopenharmony_ci reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci u16 val = swreg_value(reg); 20762306a36Sopenharmony_ci bool lm_id; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci switch (swreg_type(reg)) { 21062306a36Sopenharmony_ci case NN_REG_GPR_A: 21162306a36Sopenharmony_ci case NN_REG_GPR_B: 21262306a36Sopenharmony_ci case NN_REG_GPR_BOTH: 21362306a36Sopenharmony_ci return val; 21462306a36Sopenharmony_ci case NN_REG_XFER: 21562306a36Sopenharmony_ci return RE_REG_XFR | val; 21662306a36Sopenharmony_ci case NN_REG_LMEM: 21762306a36Sopenharmony_ci lm_id = swreg_lm_idx(reg); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) { 22062306a36Sopenharmony_ci pr_err("bad LM mode for restricted operands %d\n", 22162306a36Sopenharmony_ci swreg_lm_mode(reg)); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (val & ~RE_REG_LM_IDX_MAX) { 22662306a36Sopenharmony_ci pr_err("LM offset too large\n"); 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val; 23162306a36Sopenharmony_ci case NN_REG_IMM: 23262306a36Sopenharmony_ci if (val & ~(0x7f | has_imm8 << 7)) { 23362306a36Sopenharmony_ci pr_err("immediate too large\n"); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci *i8 = val & 0x80; 23762306a36Sopenharmony_ci return RE_REG_IMM_encode(val & 0x7f); 23862306a36Sopenharmony_ci case NN_REG_NONE: 23962306a36Sopenharmony_ci return is_dst ? RE_REG_NO_DST : REG_NONE; 24062306a36Sopenharmony_ci case NN_REG_NNR: 24162306a36Sopenharmony_ci pr_err("NNRs used with restricted encoding\n"); 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci pr_err("unrecognized reg encoding\n"); 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint swreg_to_restricted(swreg dst, swreg lreg, swreg rreg, 25062306a36Sopenharmony_ci struct nfp_insn_re_regs *reg, bool has_imm8) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci memset(reg, 0, sizeof(*reg)); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Decode destination */ 25562306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_IMM) 25662306a36Sopenharmony_ci return -EFAULT; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_GPR_B) 25962306a36Sopenharmony_ci reg->dst_ab = ALU_DST_B; 26062306a36Sopenharmony_ci if (swreg_type(dst) == NN_REG_GPR_BOTH) 26162306a36Sopenharmony_ci reg->wr_both = true; 26262306a36Sopenharmony_ci reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Decode source operands */ 26562306a36Sopenharmony_ci if (swreg_type(lreg) == swreg_type(rreg) && 26662306a36Sopenharmony_ci swreg_type(lreg) != NN_REG_NONE) 26762306a36Sopenharmony_ci return -EFAULT; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (swreg_type(lreg) == NN_REG_GPR_B || 27062306a36Sopenharmony_ci swreg_type(rreg) == NN_REG_GPR_A) { 27162306a36Sopenharmony_ci reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 27262306a36Sopenharmony_ci reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 27362306a36Sopenharmony_ci reg->swap = true; 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 27662306a36Sopenharmony_ci reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci reg->dst_lmextn = swreg_lmextn(dst); 28062306a36Sopenharmony_ci reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci#define NFP_USTORE_ECC_POLY_WORDS 7 28662306a36Sopenharmony_ci#define NFP_USTORE_OP_BITS 45 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = { 28962306a36Sopenharmony_ci 0x0ff800007fffULL, 29062306a36Sopenharmony_ci 0x11f801ff801fULL, 29162306a36Sopenharmony_ci 0x1e387e0781e1ULL, 29262306a36Sopenharmony_ci 0x17cb8e388e22ULL, 29362306a36Sopenharmony_ci 0x1af5b2c93244ULL, 29462306a36Sopenharmony_ci 0x1f56d5525488ULL, 29562306a36Sopenharmony_ci 0x0daf69a46910ULL, 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic bool parity(u64 value) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci return hweight64(value) & 1; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciint nfp_ustore_check_valid_no_ecc(u64 insn) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0)) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciu64 nfp_ustore_calc_ecc_insn(u64 insn) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci u8 ecc = 0; 31462306a36Sopenharmony_ci int i; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++) 31762306a36Sopenharmony_ci ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return insn | (u64)ecc << NFP_USTORE_OP_BITS; 32062306a36Sopenharmony_ci} 321