321 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/* Copyright (C) 2016-2018 Netronome Systems, Inc. */3 4#include <linux/bitops.h>5#include <linux/errno.h>6#include <linux/kernel.h>7#include <linux/string.h>8#include <linux/types.h>9 10#include "nfp_asm.h"11 12const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {13 [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 },14 [CMD_TGT_WRITE32_SWAP] = { 0x02, 0x5f },15 [CMD_TGT_READ8] = { 0x01, 0x43 },16 [CMD_TGT_READ32] = { 0x00, 0x5c },17 [CMD_TGT_READ32_LE] = { 0x01, 0x5c },18 [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c },19 [CMD_TGT_READ_LE] = { 0x01, 0x40 },20 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },21 [CMD_TGT_ADD] = { 0x00, 0x47 },22 [CMD_TGT_ADD_IMM] = { 0x02, 0x47 },23};24 25static bool unreg_is_imm(u16 reg)26{27 return (reg & UR_REG_IMM) == UR_REG_IMM;28}29 30u16 br_get_offset(u64 instr)31{32 u16 addr_lo, addr_hi;33 34 addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr);35 addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr);36 37 return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) |38 addr_lo;39}40 41void br_set_offset(u64 *instr, u16 offset)42{43 u16 addr_lo, addr_hi;44 45 addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO));46 addr_hi = offset != addr_lo;47 *instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO);48 *instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi);49 *instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo);50}51 52void br_add_offset(u64 *instr, u16 offset)53{54 u16 addr;55 56 addr = br_get_offset(*instr);57 br_set_offset(instr, addr + offset);58}59 60static bool immed_can_modify(u64 instr)61{62 if (FIELD_GET(OP_IMMED_INV, instr) ||63 FIELD_GET(OP_IMMED_SHIFT, instr) ||64 FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) {65 pr_err("Can't decode/encode immed!\n");66 return false;67 }68 return true;69}70 71u16 immed_get_value(u64 instr)72{73 u16 reg;74 75 if (!immed_can_modify(instr))76 return 0;77 78 reg = FIELD_GET(OP_IMMED_A_SRC, instr);79 if (!unreg_is_imm(reg))80 reg = FIELD_GET(OP_IMMED_B_SRC, instr);81 82 return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8;83}84 85void immed_set_value(u64 *instr, u16 immed)86{87 if (!immed_can_modify(*instr))88 return;89 90 if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) {91 *instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff);92 *instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff);93 } else {94 *instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff);95 *instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff);96 }97 98 *instr &= ~OP_IMMED_IMM;99 *instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8);100}101 102void immed_add_value(u64 *instr, u16 offset)103{104 u16 val;105 106 if (!immed_can_modify(*instr))107 return;108 109 val = immed_get_value(*instr);110 immed_set_value(instr, val + offset);111}112 113static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)114{115 bool lm_id, lm_dec = false;116 u16 val = swreg_value(reg);117 118 switch (swreg_type(reg)) {119 case NN_REG_GPR_A:120 case NN_REG_GPR_B:121 case NN_REG_GPR_BOTH:122 return val;123 case NN_REG_NNR:124 return UR_REG_NN | val;125 case NN_REG_XFER:126 return UR_REG_XFR | val;127 case NN_REG_LMEM:128 lm_id = swreg_lm_idx(reg);129 130 switch (swreg_lm_mode(reg)) {131 case NN_LM_MOD_NONE:132 if (val & ~UR_REG_LM_IDX_MAX) {133 pr_err("LM offset too large\n");134 return 0;135 }136 return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) |137 val;138 case NN_LM_MOD_DEC:139 lm_dec = true;140 fallthrough;141 case NN_LM_MOD_INC:142 if (val) {143 pr_err("LM offset in inc/dev mode\n");144 return 0;145 }146 return UR_REG_LM | UR_REG_LM_POST_MOD |147 FIELD_PREP(UR_REG_LM_IDX, lm_id) |148 FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec);149 default:150 pr_err("bad LM mode for unrestricted operands %d\n",151 swreg_lm_mode(reg));152 return 0;153 }154 case NN_REG_IMM:155 if (val & ~0xff) {156 pr_err("immediate too large\n");157 return 0;158 }159 return UR_REG_IMM_encode(val);160 case NN_REG_NONE:161 return is_dst ? UR_REG_NO_DST : REG_NONE;162 }163 164 pr_err("unrecognized reg encoding %08x\n", reg);165 return 0;166}167 168int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,169 struct nfp_insn_ur_regs *reg)170{171 memset(reg, 0, sizeof(*reg));172 173 /* Decode destination */174 if (swreg_type(dst) == NN_REG_IMM)175 return -EFAULT;176 177 if (swreg_type(dst) == NN_REG_GPR_B)178 reg->dst_ab = ALU_DST_B;179 if (swreg_type(dst) == NN_REG_GPR_BOTH)180 reg->wr_both = true;181 reg->dst = nfp_swreg_to_unreg(dst, true);182 183 /* Decode source operands */184 if (swreg_type(lreg) == swreg_type(rreg) &&185 swreg_type(lreg) != NN_REG_NONE)186 return -EFAULT;187 188 if (swreg_type(lreg) == NN_REG_GPR_B ||189 swreg_type(rreg) == NN_REG_GPR_A) {190 reg->areg = nfp_swreg_to_unreg(rreg, false);191 reg->breg = nfp_swreg_to_unreg(lreg, false);192 reg->swap = true;193 } else {194 reg->areg = nfp_swreg_to_unreg(lreg, false);195 reg->breg = nfp_swreg_to_unreg(rreg, false);196 }197 198 reg->dst_lmextn = swreg_lmextn(dst);199 reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);200 201 return 0;202}203 204static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8)205{206 u16 val = swreg_value(reg);207 bool lm_id;208 209 switch (swreg_type(reg)) {210 case NN_REG_GPR_A:211 case NN_REG_GPR_B:212 case NN_REG_GPR_BOTH:213 return val;214 case NN_REG_XFER:215 return RE_REG_XFR | val;216 case NN_REG_LMEM:217 lm_id = swreg_lm_idx(reg);218 219 if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) {220 pr_err("bad LM mode for restricted operands %d\n",221 swreg_lm_mode(reg));222 return 0;223 }224 225 if (val & ~RE_REG_LM_IDX_MAX) {226 pr_err("LM offset too large\n");227 return 0;228 }229 230 return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val;231 case NN_REG_IMM:232 if (val & ~(0x7f | has_imm8 << 7)) {233 pr_err("immediate too large\n");234 return 0;235 }236 *i8 = val & 0x80;237 return RE_REG_IMM_encode(val & 0x7f);238 case NN_REG_NONE:239 return is_dst ? RE_REG_NO_DST : REG_NONE;240 case NN_REG_NNR:241 pr_err("NNRs used with restricted encoding\n");242 return 0;243 }244 245 pr_err("unrecognized reg encoding\n");246 return 0;247}248 249int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,250 struct nfp_insn_re_regs *reg, bool has_imm8)251{252 memset(reg, 0, sizeof(*reg));253 254 /* Decode destination */255 if (swreg_type(dst) == NN_REG_IMM)256 return -EFAULT;257 258 if (swreg_type(dst) == NN_REG_GPR_B)259 reg->dst_ab = ALU_DST_B;260 if (swreg_type(dst) == NN_REG_GPR_BOTH)261 reg->wr_both = true;262 reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL);263 264 /* Decode source operands */265 if (swreg_type(lreg) == swreg_type(rreg) &&266 swreg_type(lreg) != NN_REG_NONE)267 return -EFAULT;268 269 if (swreg_type(lreg) == NN_REG_GPR_B ||270 swreg_type(rreg) == NN_REG_GPR_A) {271 reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8);272 reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8);273 reg->swap = true;274 } else {275 reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8);276 reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8);277 }278 279 reg->dst_lmextn = swreg_lmextn(dst);280 reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);281 282 return 0;283}284 285#define NFP_USTORE_ECC_POLY_WORDS 7286#define NFP_USTORE_OP_BITS 45287 288static const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = {289 0x0ff800007fffULL,290 0x11f801ff801fULL,291 0x1e387e0781e1ULL,292 0x17cb8e388e22ULL,293 0x1af5b2c93244ULL,294 0x1f56d5525488ULL,295 0x0daf69a46910ULL,296};297 298static bool parity(u64 value)299{300 return hweight64(value) & 1;301}302 303int nfp_ustore_check_valid_no_ecc(u64 insn)304{305 if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0))306 return -EINVAL;307 308 return 0;309}310 311u64 nfp_ustore_calc_ecc_insn(u64 insn)312{313 u8 ecc = 0;314 int i;315 316 for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++)317 ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i;318 319 return insn | (u64)ecc << NFP_USTORE_OP_BITS;320}321