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 | |
12 | const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { |
13 | [CMD_TGT_WRITE8_SWAP] = { .token: 0x02, .tgt_cmd: 0x42 }, |
14 | [CMD_TGT_WRITE32_SWAP] = { .token: 0x02, .tgt_cmd: 0x5f }, |
15 | [CMD_TGT_READ8] = { .token: 0x01, .tgt_cmd: 0x43 }, |
16 | [CMD_TGT_READ32] = { .token: 0x00, .tgt_cmd: 0x5c }, |
17 | [CMD_TGT_READ32_LE] = { .token: 0x01, .tgt_cmd: 0x5c }, |
18 | [CMD_TGT_READ32_SWAP] = { .token: 0x02, .tgt_cmd: 0x5c }, |
19 | [CMD_TGT_READ_LE] = { .token: 0x01, .tgt_cmd: 0x40 }, |
20 | [CMD_TGT_READ_SWAP_LE] = { .token: 0x03, .tgt_cmd: 0x40 }, |
21 | [CMD_TGT_ADD] = { .token: 0x00, .tgt_cmd: 0x47 }, |
22 | [CMD_TGT_ADD_IMM] = { .token: 0x02, .tgt_cmd: 0x47 }, |
23 | }; |
24 | |
25 | static bool unreg_is_imm(u16 reg) |
26 | { |
27 | return (reg & UR_REG_IMM) == UR_REG_IMM; |
28 | } |
29 | |
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: *instr); |
57 | br_set_offset(instr, offset: addr + offset); |
58 | } |
59 | |
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 | |
82 | return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; |
83 | } |
84 | |
85 | void immed_set_value(u64 *instr, u16 immed) |
86 | { |
87 | if (!immed_can_modify(instr: *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: *instr)) |
107 | return; |
108 | |
109 | val = immed_get_value(instr: *instr); |
110 | immed_set_value(instr, immed: val + offset); |
111 | } |
112 | |
113 | static 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 | |
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(reg: dst) == NN_REG_IMM) |
175 | return -EFAULT; |
176 | |
177 | if (swreg_type(reg: dst) == NN_REG_GPR_B) |
178 | reg->dst_ab = ALU_DST_B; |
179 | if (swreg_type(reg: dst) == NN_REG_GPR_BOTH) |
180 | reg->wr_both = true; |
181 | reg->dst = nfp_swreg_to_unreg(reg: dst, is_dst: true); |
182 | |
183 | /* Decode source operands */ |
184 | if (swreg_type(reg: lreg) == swreg_type(reg: rreg) && |
185 | swreg_type(reg: lreg) != NN_REG_NONE) |
186 | return -EFAULT; |
187 | |
188 | if (swreg_type(reg: lreg) == NN_REG_GPR_B || |
189 | swreg_type(reg: rreg) == NN_REG_GPR_A) { |
190 | reg->areg = nfp_swreg_to_unreg(reg: rreg, is_dst: false); |
191 | reg->breg = nfp_swreg_to_unreg(reg: lreg, is_dst: false); |
192 | reg->swap = true; |
193 | } else { |
194 | reg->areg = nfp_swreg_to_unreg(reg: lreg, is_dst: false); |
195 | reg->breg = nfp_swreg_to_unreg(reg: rreg, is_dst: false); |
196 | } |
197 | |
198 | reg->dst_lmextn = swreg_lmextn(reg: dst); |
199 | reg->src_lmextn = swreg_lmextn(reg: lreg) || swreg_lmextn(reg: rreg); |
200 | |
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); |
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 | |
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(reg: dst) == NN_REG_IMM) |
256 | return -EFAULT; |
257 | |
258 | if (swreg_type(reg: dst) == NN_REG_GPR_B) |
259 | reg->dst_ab = ALU_DST_B; |
260 | if (swreg_type(reg: dst) == NN_REG_GPR_BOTH) |
261 | reg->wr_both = true; |
262 | reg->dst = nfp_swreg_to_rereg(reg: dst, is_dst: true, has_imm8: false, NULL); |
263 | |
264 | /* Decode source operands */ |
265 | if (swreg_type(reg: lreg) == swreg_type(reg: rreg) && |
266 | swreg_type(reg: lreg) != NN_REG_NONE) |
267 | return -EFAULT; |
268 | |
269 | if (swreg_type(reg: lreg) == NN_REG_GPR_B || |
270 | swreg_type(reg: rreg) == NN_REG_GPR_A) { |
271 | reg->areg = nfp_swreg_to_rereg(reg: rreg, is_dst: false, has_imm8, i8: ®->i8); |
272 | reg->breg = nfp_swreg_to_rereg(reg: lreg, is_dst: false, has_imm8, i8: ®->i8); |
273 | reg->swap = true; |
274 | } else { |
275 | reg->areg = nfp_swreg_to_rereg(reg: lreg, is_dst: false, has_imm8, i8: ®->i8); |
276 | reg->breg = nfp_swreg_to_rereg(reg: rreg, is_dst: false, has_imm8, i8: ®->i8); |
277 | } |
278 | |
279 | reg->dst_lmextn = swreg_lmextn(reg: dst); |
280 | reg->src_lmextn = swreg_lmextn(reg: lreg) || swreg_lmextn(reg: rreg); |
281 | |
282 | return 0; |
283 | } |
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(value: nfp_ustore_ecc_polynomials[i] & insn) << i; |
318 | |
319 | return insn | (u64)ecc << NFP_USTORE_OP_BITS; |
320 | } |
321 | |