nfp_asm.c (7495B)
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 7 286#define NFP_USTORE_OP_BITS 45 287 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}