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 }; 52 53 static bool unreg_is_imm(u16 reg) 54 { 55 return (reg & UR_REG_IMM) == UR_REG_IMM; 56 } 57 58 u16 br_get_offset(u64 instr) 59 { 60 u16 addr_lo, addr_hi; 61 62 addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr); 63 addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr); 64 65 return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) | 66 addr_lo; 67 } 68 69 void br_set_offset(u64 *instr, u16 offset) 70 { 71 u16 addr_lo, addr_hi; 72 73 addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); 74 addr_hi = offset != addr_lo; 75 *instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO); 76 *instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi); 77 *instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo); 78 } 79 80 void br_add_offset(u64 *instr, u16 offset) 81 { 82 u16 addr; 83 84 addr = br_get_offset(*instr); 85 br_set_offset(instr, addr + offset); 86 } 87 88 static bool immed_can_modify(u64 instr) 89 { 90 if (FIELD_GET(OP_IMMED_INV, instr) || 91 FIELD_GET(OP_IMMED_SHIFT, instr) || 92 FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) { 93 pr_err("Can't decode/encode immed!\n"); 94 return false; 95 } 96 return true; 97 } 98 99 u16 immed_get_value(u64 instr) 100 { 101 u16 reg; 102 103 if (!immed_can_modify(instr)) 104 return 0; 105 106 reg = FIELD_GET(OP_IMMED_A_SRC, instr); 107 if (!unreg_is_imm(reg)) 108 reg = FIELD_GET(OP_IMMED_B_SRC, instr); 109 110 return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; 111 } 112 113 void immed_set_value(u64 *instr, u16 immed) 114 { 115 if (!immed_can_modify(*instr)) 116 return; 117 118 if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) { 119 *instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff); 120 *instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff); 121 } else { 122 *instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff); 123 *instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff); 124 } 125 126 *instr &= ~OP_IMMED_IMM; 127 *instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8); 128 } 129 130 void immed_add_value(u64 *instr, u16 offset) 131 { 132 u16 val; 133 134 if (!immed_can_modify(*instr)) 135 return; 136 137 val = immed_get_value(*instr); 138 immed_set_value(instr, val + offset); 139 } 140 141 static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst) 142 { 143 bool lm_id, lm_dec = false; 144 u16 val = swreg_value(reg); 145 146 switch (swreg_type(reg)) { 147 case NN_REG_GPR_A: 148 case NN_REG_GPR_B: 149 case NN_REG_GPR_BOTH: 150 return val; 151 case NN_REG_NNR: 152 return UR_REG_NN | val; 153 case NN_REG_XFER: 154 return UR_REG_XFR | val; 155 case NN_REG_LMEM: 156 lm_id = swreg_lm_idx(reg); 157 158 switch (swreg_lm_mode(reg)) { 159 case NN_LM_MOD_NONE: 160 if (val & ~UR_REG_LM_IDX_MAX) { 161 pr_err("LM offset too large\n"); 162 return 0; 163 } 164 return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) | 165 val; 166 case NN_LM_MOD_DEC: 167 lm_dec = true; 168 /* fall through */ 169 case NN_LM_MOD_INC: 170 if (val) { 171 pr_err("LM offset in inc/dev mode\n"); 172 return 0; 173 } 174 return UR_REG_LM | UR_REG_LM_POST_MOD | 175 FIELD_PREP(UR_REG_LM_IDX, lm_id) | 176 FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec); 177 default: 178 pr_err("bad LM mode for unrestricted operands %d\n", 179 swreg_lm_mode(reg)); 180 return 0; 181 } 182 case NN_REG_IMM: 183 if (val & ~0xff) { 184 pr_err("immediate too large\n"); 185 return 0; 186 } 187 return UR_REG_IMM_encode(val); 188 case NN_REG_NONE: 189 return is_dst ? UR_REG_NO_DST : REG_NONE; 190 } 191 192 pr_err("unrecognized reg encoding %08x\n", reg); 193 return 0; 194 } 195 196 int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg, 197 struct nfp_insn_ur_regs *reg) 198 { 199 memset(reg, 0, sizeof(*reg)); 200 201 /* Decode destination */ 202 if (swreg_type(dst) == NN_REG_IMM) 203 return -EFAULT; 204 205 if (swreg_type(dst) == NN_REG_GPR_B) 206 reg->dst_ab = ALU_DST_B; 207 if (swreg_type(dst) == NN_REG_GPR_BOTH) 208 reg->wr_both = true; 209 reg->dst = nfp_swreg_to_unreg(dst, true); 210 211 /* Decode source operands */ 212 if (swreg_type(lreg) == swreg_type(rreg) && 213 swreg_type(lreg) != NN_REG_NONE) 214 return -EFAULT; 215 216 if (swreg_type(lreg) == NN_REG_GPR_B || 217 swreg_type(rreg) == NN_REG_GPR_A) { 218 reg->areg = nfp_swreg_to_unreg(rreg, false); 219 reg->breg = nfp_swreg_to_unreg(lreg, false); 220 reg->swap = true; 221 } else { 222 reg->areg = nfp_swreg_to_unreg(lreg, false); 223 reg->breg = nfp_swreg_to_unreg(rreg, false); 224 } 225 226 reg->dst_lmextn = swreg_lmextn(dst); 227 reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); 228 229 return 0; 230 } 231 232 static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8) 233 { 234 u16 val = swreg_value(reg); 235 bool lm_id; 236 237 switch (swreg_type(reg)) { 238 case NN_REG_GPR_A: 239 case NN_REG_GPR_B: 240 case NN_REG_GPR_BOTH: 241 return val; 242 case NN_REG_XFER: 243 return RE_REG_XFR | val; 244 case NN_REG_LMEM: 245 lm_id = swreg_lm_idx(reg); 246 247 if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) { 248 pr_err("bad LM mode for restricted operands %d\n", 249 swreg_lm_mode(reg)); 250 return 0; 251 } 252 253 if (val & ~RE_REG_LM_IDX_MAX) { 254 pr_err("LM offset too large\n"); 255 return 0; 256 } 257 258 return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val; 259 case NN_REG_IMM: 260 if (val & ~(0x7f | has_imm8 << 7)) { 261 pr_err("immediate too large\n"); 262 return 0; 263 } 264 *i8 = val & 0x80; 265 return RE_REG_IMM_encode(val & 0x7f); 266 case NN_REG_NONE: 267 return is_dst ? RE_REG_NO_DST : REG_NONE; 268 case NN_REG_NNR: 269 pr_err("NNRs used with restricted encoding\n"); 270 return 0; 271 } 272 273 pr_err("unrecognized reg encoding\n"); 274 return 0; 275 } 276 277 int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg, 278 struct nfp_insn_re_regs *reg, bool has_imm8) 279 { 280 memset(reg, 0, sizeof(*reg)); 281 282 /* Decode destination */ 283 if (swreg_type(dst) == NN_REG_IMM) 284 return -EFAULT; 285 286 if (swreg_type(dst) == NN_REG_GPR_B) 287 reg->dst_ab = ALU_DST_B; 288 if (swreg_type(dst) == NN_REG_GPR_BOTH) 289 reg->wr_both = true; 290 reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL); 291 292 /* Decode source operands */ 293 if (swreg_type(lreg) == swreg_type(rreg) && 294 swreg_type(lreg) != NN_REG_NONE) 295 return -EFAULT; 296 297 if (swreg_type(lreg) == NN_REG_GPR_B || 298 swreg_type(rreg) == NN_REG_GPR_A) { 299 reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 300 reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 301 reg->swap = true; 302 } else { 303 reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, ®->i8); 304 reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, ®->i8); 305 } 306 307 reg->dst_lmextn = swreg_lmextn(dst); 308 reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); 309 310 return 0; 311 } 312 313 #define NFP_USTORE_ECC_POLY_WORDS 7 314 #define NFP_USTORE_OP_BITS 45 315 316 static const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = { 317 0x0ff800007fffULL, 318 0x11f801ff801fULL, 319 0x1e387e0781e1ULL, 320 0x17cb8e388e22ULL, 321 0x1af5b2c93244ULL, 322 0x1f56d5525488ULL, 323 0x0daf69a46910ULL, 324 }; 325 326 static bool parity(u64 value) 327 { 328 return hweight64(value) & 1; 329 } 330 331 int nfp_ustore_check_valid_no_ecc(u64 insn) 332 { 333 if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0)) 334 return -EINVAL; 335 336 return 0; 337 } 338 339 u64 nfp_ustore_calc_ecc_insn(u64 insn) 340 { 341 u8 ecc = 0; 342 int i; 343 344 for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++) 345 ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i; 346 347 return insn | (u64)ecc << NFP_USTORE_OP_BITS; 348 } 349