1 /* 2 * Copyright (C) 2016-2017 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 #include <linux/bitops.h> 35 #include <linux/errno.h> 36 #include <linux/kernel.h> 37 #include <linux/string.h> 38 #include <linux/types.h> 39 40 #include "nfp_asm.h" 41 42 const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { 43 [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 }, 44 [CMD_TGT_WRITE32_SWAP] = { 0x02, 0x5f }, 45 [CMD_TGT_READ8] = { 0x01, 0x43 }, 46 [CMD_TGT_READ32] = { 0x00, 0x5c }, 47 [CMD_TGT_READ32_LE] = { 0x01, 0x5c }, 48 [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c }, 49 [CMD_TGT_READ_LE] = { 0x01, 0x40 }, 50 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, 51 [CMD_TGT_ADD] = { 0x00, 0x47 }, 52 [CMD_TGT_ADD_IMM] = { 0x02, 0x47 }, 53 }; 54 55 static bool unreg_is_imm(u16 reg) 56 { 57 return (reg & UR_REG_IMM) == UR_REG_IMM; 58 } 59 60 u16 br_get_offset(u64 instr) 61 { 62 u16 addr_lo, addr_hi; 63 64 addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr); 65 addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr); 66 67 return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) | 68 addr_lo; 69 } 70 71 void br_set_offset(u64 *instr, u16 offset) 72 { 73 u16 addr_lo, addr_hi; 74 75 addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); 76 addr_hi = offset != addr_lo; 77 *instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO); 78 *instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi); 79 *instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo); 80 } 81 82 void br_add_offset(u64 *instr, u16 offset) 83 { 84 u16 addr; 85 86 addr = br_get_offset(*instr); 87 br_set_offset(instr, addr + offset); 88 } 89 90 static bool immed_can_modify(u64 instr) 91 { 92 if (FIELD_GET(OP_IMMED_INV, instr) || 93 FIELD_GET(OP_IMMED_SHIFT, instr) || 94 FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) { 95 pr_err("Can't decode/encode immed!\n"); 96 return false; 97 } 98 return true; 99 } 100 101 u16 immed_get_value(u64 instr) 102 { 103 u16 reg; 104 105 if (!immed_can_modify(instr)) 106 return 0; 107 108 reg = FIELD_GET(OP_IMMED_A_SRC, instr); 109 if (!unreg_is_imm(reg)) 110 reg = FIELD_GET(OP_IMMED_B_SRC, instr); 111 112 return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; 113 } 114 115 void immed_set_value(u64 *instr, u16 immed) 116 { 117 if (!immed_can_modify(*instr)) 118 return; 119 120 if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) { 121 *instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff); 122 *instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff); 123 } else { 124 *instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff); 125 *instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff); 126 } 127 128 *instr &= ~OP_IMMED_IMM; 129 *instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8); 130 } 131 132 void immed_add_value(u64 *instr, u16 offset) 133 { 134 u16 val; 135 136 if (!immed_can_modify(*instr)) 137 return; 138 139 val = immed_get_value(*instr); 140 immed_set_value(instr, val + offset); 141 } 142 143 static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst) 144 { 145 bool lm_id, lm_dec = false; 146 u16 val = swreg_value(reg); 147 148 switch (swreg_type(reg)) { 149 case NN_REG_GPR_A: 150 case NN_REG_GPR_B: 151 case NN_REG_GPR_BOTH: 152 return val; 153 case NN_REG_NNR: 154 return UR_REG_NN | val; 155 case NN_REG_XFER: 156 return UR_REG_XFR | val; 157 case NN_REG_LMEM: 158 lm_id = swreg_lm_idx(reg); 159 160 switch (swreg_lm_mode(reg)) { 161 case NN_LM_MOD_NONE: 162 if (val & ~UR_REG_LM_IDX_MAX) { 163 pr_err("LM offset too large\n"); 164 return 0; 165 } 166 return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) | 167 val; 168 case NN_LM_MOD_DEC: 169 lm_dec = true; 170 /* fall through */ 171 case NN_LM_MOD_INC: 172 if (val) { 173 pr_err("LM offset in inc/dev mode\n"); 174 return 0; 175 } 176 return UR_REG_LM | UR_REG_LM_POST_MOD | 177 FIELD_PREP(UR_REG_LM_IDX, lm_id) | 178 FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec); 179 default: 180 pr_err("bad LM mode for unrestricted operands %d\n", 181 swreg_lm_mode(reg)); 182 return 0; 183 } 184 case NN_REG_IMM: 185 if (val & ~0xff) { 186 pr_err("immediate too large\n"); 187 return 0; 188 } 189 return UR_REG_IMM_encode(val); 190 case NN_REG_NONE: 191 return is_dst ? UR_REG_NO_DST : REG_NONE; 192 } 193 194 pr_err("unrecognized reg encoding %08x\n", reg); 195 return 0; 196 } 197 198 int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg, 199 struct nfp_insn_ur_regs *reg) 200 { 201 memset(reg, 0, sizeof(*reg)); 202 203 /* Decode destination */ 204 if (swreg_type(dst) == NN_REG_IMM) 205 return -EFAULT; 206 207 if (swreg_type(dst) == NN_REG_GPR_B) 208 reg->dst_ab = ALU_DST_B; 209 if (swreg_type(dst) == NN_REG_GPR_BOTH) 210 reg->wr_both = true; 211 reg->dst = nfp_swreg_to_unreg(dst, true); 212 213 /* Decode source operands */ 214 if (swreg_type(lreg) == swreg_type(rreg) && 215 swreg_type(lreg) != NN_REG_NONE) 216 return -EFAULT; 217 218 if (swreg_type(lreg) == NN_REG_GPR_B || 219 swreg_type(rreg) == NN_REG_GPR_A) { 220 reg->areg = nfp_swreg_to_unreg(rreg, false); 221 reg->breg = nfp_swreg_to_unreg(lreg, false); 222 reg->swap = true; 223 } else { 224 reg->areg = nfp_swreg_to_unreg(lreg, false); 225 reg->breg = nfp_swreg_to_unreg(rreg, false); 226 } 227 228 reg->dst_lmextn = swreg_lmextn(dst); 229 reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); 230 231 return 0; 232 } 233 234 static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8) 235 { 236 u16 val = swreg_value(reg); 237 bool lm_id; 238 239 switch (swreg_type(reg)) { 240 case NN_REG_GPR_A: 241 case NN_REG_GPR_B: 242 case NN_REG_GPR_BOTH: 243 return val; 244 case NN_REG_XFER: 245 return RE_REG_XFR | val; 246 case NN_REG_LMEM: 247 lm_id = swreg_lm_idx(reg); 248 249 if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) { 250 pr_err("bad LM mode for restricted operands %d\n", 251 swreg_lm_mode(reg)); 252 return 0; 253 } 254 255 if (val & ~RE_REG_LM_IDX_MAX) { 256 pr_err("LM offset too large\n"); 257 return 0; 258 } 259 260 return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val; 261 case NN_REG_IMM: 262 if (val & ~(0x7f | has_imm8 << 7)) { 263 pr_err("immediate too large\n"); 264 return 0; 265 } 266 *i8 = val & 0x80; 267 return RE_REG_IMM_encode(val & 0x7f); 268 case NN_REG_NONE: 269 return is_dst ? RE_REG_NO_DST : REG_NONE; 270 case NN_REG_NNR: 271 pr_err("NNRs used with restricted encoding\n"); 272 return 0; 273 } 274 275 pr_err("unrecognized reg encoding\n"); 276 return 0; 277 } 278 279 int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg, 280 struct nfp_insn_re_regs *reg, bool has_imm8) 281 { 282 memset(reg, 0, sizeof(*reg)); 283 284 /* Decode destination */ 285 if (swreg_type(dst) == NN_REG_IMM) 286 return -EFAULT; 287 288 if (swreg_type(dst) == NN_REG_GPR_B) 289 reg->dst_ab = ALU_DST_B; 290 if (swreg_type(dst) == NN_REG_GPR_BOTH) 291 reg->wr_both = true; 292 reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL); 293 294 /* Decode source operands */ 295 if (swreg_type(lreg) == swreg_type(rreg) && 296 swreg_type(lreg) != NN_REG_NONE) 297 return -EFAULT; 298 299 if (swreg_type(lreg) == NN_REG_GPR_B || 300 swreg_type(rreg) == NN_REG_GPR_A) { 301 reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 302 reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 303 reg->swap = true; 304 } else { 305 reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 306 reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 307 } 308 309 reg->dst_lmextn = swreg_lmextn(dst); 310 reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); 311 312 return 0; 313 } 314 315 #define NFP_USTORE_ECC_POLY_WORDS 7 316 #define NFP_USTORE_OP_BITS 45 317 318 static const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = { 319 0x0ff800007fffULL, 320 0x11f801ff801fULL, 321 0x1e387e0781e1ULL, 322 0x17cb8e388e22ULL, 323 0x1af5b2c93244ULL, 324 0x1f56d5525488ULL, 325 0x0daf69a46910ULL, 326 }; 327 328 static bool parity(u64 value) 329 { 330 return hweight64(value) & 1; 331 } 332 333 int nfp_ustore_check_valid_no_ecc(u64 insn) 334 { 335 if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0)) 336 return -EINVAL; 337 338 return 0; 339 } 340 341 u64 nfp_ustore_calc_ecc_insn(u64 insn) 342 { 343 u8 ecc = 0; 344 int i; 345 346 for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++) 347 ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i; 348 349 return insn | (u64)ecc << NFP_USTORE_OP_BITS; 350 } 351