Commit | Line | Data |
---|---|---|
96de2506 JK |
1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2016-2018 Netronome Systems, Inc. */ | |
2a15bb1a JK |
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 | ||
12 | const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { | |
e663fe38 | 13 | [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 }, |
9879a381 | 14 | [CMD_TGT_WRITE32_SWAP] = { 0x02, 0x5f }, |
2a15bb1a | 15 | [CMD_TGT_READ8] = { 0x01, 0x43 }, |
2ca71441 JK |
16 | [CMD_TGT_READ32] = { 0x00, 0x5c }, |
17 | [CMD_TGT_READ32_LE] = { 0x01, 0x5c }, | |
18 | [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c }, | |
2a15bb1a JK |
19 | [CMD_TGT_READ_LE] = { 0x01, 0x40 }, |
20 | [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, | |
41aed09c | 21 | [CMD_TGT_ADD] = { 0x00, 0x47 }, |
dcb0c27f | 22 | [CMD_TGT_ADD_IMM] = { 0x02, 0x47 }, |
2a15bb1a JK |
23 | }; |
24 | ||
ce4ebfd8 JK |
25 | static bool unreg_is_imm(u16 reg) |
26 | { | |
27 | return (reg & UR_REG_IMM) == UR_REG_IMM; | |
28 | } | |
29 | ||
488feeaf JK |
30 | u16 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 | ||
41 | void 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 | ||
52 | void 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 | ||
ce4ebfd8 JK |
60 | static 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 | ||
71 | u16 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 | ||
b7d99235 | 82 | return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; |
ce4ebfd8 JK |
83 | } |
84 | ||
85 | void 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 | ||
102 | void 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 | ||
2a15bb1a JK |
113 | static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst) |
114 | { | |
9f15d0f4 | 115 | bool lm_id, lm_dec = false; |
2a15bb1a JK |
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; | |
9f15d0f4 JK |
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; | |
df561f66 | 140 | fallthrough; |
9f15d0f4 JK |
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 | } | |
2a15bb1a JK |
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 | ||
168 | int 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 */ | |
08859f15 JW |
184 | if (swreg_type(lreg) == swreg_type(rreg) && |
185 | swreg_type(lreg) != NN_REG_NONE) | |
2a15bb1a JK |
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 | ||
995e101f JK |
198 | reg->dst_lmextn = swreg_lmextn(dst); |
199 | reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); | |
200 | ||
2a15bb1a JK |
201 | return 0; |
202 | } | |
203 | ||
204 | static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8) | |
205 | { | |
206 | u16 val = swreg_value(reg); | |
9f15d0f4 | 207 | bool lm_id; |
2a15bb1a JK |
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; | |
9f15d0f4 JK |
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; | |
2a15bb1a JK |
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 | ||
249 | int 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 */ | |
08859f15 JW |
265 | if (swreg_type(lreg) == swreg_type(rreg) && |
266 | swreg_type(lreg) != NN_REG_NONE) | |
2a15bb1a JK |
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 | ||
995e101f JK |
279 | reg->dst_lmextn = swreg_lmextn(dst); |
280 | reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg); | |
281 | ||
2a15bb1a JK |
282 | return 0; |
283 | } | |
fd068ddc JK |
284 | |
285 | #define NFP_USTORE_ECC_POLY_WORDS 7 | |
286 | #define NFP_USTORE_OP_BITS 45 | |
287 | ||
288 | static 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 | ||
298 | static bool parity(u64 value) | |
299 | { | |
300 | return hweight64(value) & 1; | |
301 | } | |
302 | ||
303 | int 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 | ||
311 | u64 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 | } |