162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2016-2018 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitops.h>
562306a36Sopenharmony_ci#include <linux/errno.h>
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/string.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "nfp_asm.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciconst struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
1362306a36Sopenharmony_ci	[CMD_TGT_WRITE8_SWAP] =		{ 0x02, 0x42 },
1462306a36Sopenharmony_ci	[CMD_TGT_WRITE32_SWAP] =	{ 0x02, 0x5f },
1562306a36Sopenharmony_ci	[CMD_TGT_READ8] =		{ 0x01, 0x43 },
1662306a36Sopenharmony_ci	[CMD_TGT_READ32] =		{ 0x00, 0x5c },
1762306a36Sopenharmony_ci	[CMD_TGT_READ32_LE] =		{ 0x01, 0x5c },
1862306a36Sopenharmony_ci	[CMD_TGT_READ32_SWAP] =		{ 0x02, 0x5c },
1962306a36Sopenharmony_ci	[CMD_TGT_READ_LE] =		{ 0x01, 0x40 },
2062306a36Sopenharmony_ci	[CMD_TGT_READ_SWAP_LE] =	{ 0x03, 0x40 },
2162306a36Sopenharmony_ci	[CMD_TGT_ADD] =			{ 0x00, 0x47 },
2262306a36Sopenharmony_ci	[CMD_TGT_ADD_IMM] =		{ 0x02, 0x47 },
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic bool unreg_is_imm(u16 reg)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return (reg & UR_REG_IMM) == UR_REG_IMM;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciu16 br_get_offset(u64 instr)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	u16 addr_lo, addr_hi;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr);
3562306a36Sopenharmony_ci	addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) |
3862306a36Sopenharmony_ci		addr_lo;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civoid br_set_offset(u64 *instr, u16 offset)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	u16 addr_lo, addr_hi;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO));
4662306a36Sopenharmony_ci	addr_hi = offset != addr_lo;
4762306a36Sopenharmony_ci	*instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO);
4862306a36Sopenharmony_ci	*instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi);
4962306a36Sopenharmony_ci	*instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_civoid br_add_offset(u64 *instr, u16 offset)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	u16 addr;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	addr = br_get_offset(*instr);
5762306a36Sopenharmony_ci	br_set_offset(instr, addr + offset);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic bool immed_can_modify(u64 instr)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	if (FIELD_GET(OP_IMMED_INV, instr) ||
6362306a36Sopenharmony_ci	    FIELD_GET(OP_IMMED_SHIFT, instr) ||
6462306a36Sopenharmony_ci	    FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) {
6562306a36Sopenharmony_ci		pr_err("Can't decode/encode immed!\n");
6662306a36Sopenharmony_ci		return false;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	return true;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciu16 immed_get_value(u64 instr)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u16 reg;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!immed_can_modify(instr))
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	reg = FIELD_GET(OP_IMMED_A_SRC, instr);
7962306a36Sopenharmony_ci	if (!unreg_is_imm(reg))
8062306a36Sopenharmony_ci		reg = FIELD_GET(OP_IMMED_B_SRC, instr);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid immed_set_value(u64 *instr, u16 immed)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (!immed_can_modify(*instr))
8862306a36Sopenharmony_ci		return;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) {
9162306a36Sopenharmony_ci		*instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff);
9262306a36Sopenharmony_ci		*instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff);
9362306a36Sopenharmony_ci	} else {
9462306a36Sopenharmony_ci		*instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff);
9562306a36Sopenharmony_ci		*instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	*instr &= ~OP_IMMED_IMM;
9962306a36Sopenharmony_ci	*instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid immed_add_value(u64 *instr, u16 offset)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u16 val;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!immed_can_modify(*instr))
10762306a36Sopenharmony_ci		return;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	val = immed_get_value(*instr);
11062306a36Sopenharmony_ci	immed_set_value(instr, val + offset);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	bool lm_id, lm_dec = false;
11662306a36Sopenharmony_ci	u16 val = swreg_value(reg);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	switch (swreg_type(reg)) {
11962306a36Sopenharmony_ci	case NN_REG_GPR_A:
12062306a36Sopenharmony_ci	case NN_REG_GPR_B:
12162306a36Sopenharmony_ci	case NN_REG_GPR_BOTH:
12262306a36Sopenharmony_ci		return val;
12362306a36Sopenharmony_ci	case NN_REG_NNR:
12462306a36Sopenharmony_ci		return UR_REG_NN | val;
12562306a36Sopenharmony_ci	case NN_REG_XFER:
12662306a36Sopenharmony_ci		return UR_REG_XFR | val;
12762306a36Sopenharmony_ci	case NN_REG_LMEM:
12862306a36Sopenharmony_ci		lm_id = swreg_lm_idx(reg);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		switch (swreg_lm_mode(reg)) {
13162306a36Sopenharmony_ci		case NN_LM_MOD_NONE:
13262306a36Sopenharmony_ci			if (val & ~UR_REG_LM_IDX_MAX) {
13362306a36Sopenharmony_ci				pr_err("LM offset too large\n");
13462306a36Sopenharmony_ci				return 0;
13562306a36Sopenharmony_ci			}
13662306a36Sopenharmony_ci			return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) |
13762306a36Sopenharmony_ci				val;
13862306a36Sopenharmony_ci		case NN_LM_MOD_DEC:
13962306a36Sopenharmony_ci			lm_dec = true;
14062306a36Sopenharmony_ci			fallthrough;
14162306a36Sopenharmony_ci		case NN_LM_MOD_INC:
14262306a36Sopenharmony_ci			if (val) {
14362306a36Sopenharmony_ci				pr_err("LM offset in inc/dev mode\n");
14462306a36Sopenharmony_ci				return 0;
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci			return UR_REG_LM | UR_REG_LM_POST_MOD |
14762306a36Sopenharmony_ci				FIELD_PREP(UR_REG_LM_IDX, lm_id) |
14862306a36Sopenharmony_ci				FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec);
14962306a36Sopenharmony_ci		default:
15062306a36Sopenharmony_ci			pr_err("bad LM mode for unrestricted operands %d\n",
15162306a36Sopenharmony_ci			       swreg_lm_mode(reg));
15262306a36Sopenharmony_ci			return 0;
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci	case NN_REG_IMM:
15562306a36Sopenharmony_ci		if (val & ~0xff) {
15662306a36Sopenharmony_ci			pr_err("immediate too large\n");
15762306a36Sopenharmony_ci			return 0;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci		return UR_REG_IMM_encode(val);
16062306a36Sopenharmony_ci	case NN_REG_NONE:
16162306a36Sopenharmony_ci		return is_dst ? UR_REG_NO_DST : REG_NONE;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	pr_err("unrecognized reg encoding %08x\n", reg);
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,
16962306a36Sopenharmony_ci			  struct nfp_insn_ur_regs *reg)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	memset(reg, 0, sizeof(*reg));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Decode destination */
17462306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_IMM)
17562306a36Sopenharmony_ci		return -EFAULT;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_GPR_B)
17862306a36Sopenharmony_ci		reg->dst_ab = ALU_DST_B;
17962306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_GPR_BOTH)
18062306a36Sopenharmony_ci		reg->wr_both = true;
18162306a36Sopenharmony_ci	reg->dst = nfp_swreg_to_unreg(dst, true);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Decode source operands */
18462306a36Sopenharmony_ci	if (swreg_type(lreg) == swreg_type(rreg) &&
18562306a36Sopenharmony_ci	    swreg_type(lreg) != NN_REG_NONE)
18662306a36Sopenharmony_ci		return -EFAULT;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (swreg_type(lreg) == NN_REG_GPR_B ||
18962306a36Sopenharmony_ci	    swreg_type(rreg) == NN_REG_GPR_A) {
19062306a36Sopenharmony_ci		reg->areg = nfp_swreg_to_unreg(rreg, false);
19162306a36Sopenharmony_ci		reg->breg = nfp_swreg_to_unreg(lreg, false);
19262306a36Sopenharmony_ci		reg->swap = true;
19362306a36Sopenharmony_ci	} else {
19462306a36Sopenharmony_ci		reg->areg = nfp_swreg_to_unreg(lreg, false);
19562306a36Sopenharmony_ci		reg->breg = nfp_swreg_to_unreg(rreg, false);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	reg->dst_lmextn = swreg_lmextn(dst);
19962306a36Sopenharmony_ci	reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	u16 val = swreg_value(reg);
20762306a36Sopenharmony_ci	bool lm_id;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	switch (swreg_type(reg)) {
21062306a36Sopenharmony_ci	case NN_REG_GPR_A:
21162306a36Sopenharmony_ci	case NN_REG_GPR_B:
21262306a36Sopenharmony_ci	case NN_REG_GPR_BOTH:
21362306a36Sopenharmony_ci		return val;
21462306a36Sopenharmony_ci	case NN_REG_XFER:
21562306a36Sopenharmony_ci		return RE_REG_XFR | val;
21662306a36Sopenharmony_ci	case NN_REG_LMEM:
21762306a36Sopenharmony_ci		lm_id = swreg_lm_idx(reg);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) {
22062306a36Sopenharmony_ci			pr_err("bad LM mode for restricted operands %d\n",
22162306a36Sopenharmony_ci			       swreg_lm_mode(reg));
22262306a36Sopenharmony_ci			return 0;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		if (val & ~RE_REG_LM_IDX_MAX) {
22662306a36Sopenharmony_ci			pr_err("LM offset too large\n");
22762306a36Sopenharmony_ci			return 0;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val;
23162306a36Sopenharmony_ci	case NN_REG_IMM:
23262306a36Sopenharmony_ci		if (val & ~(0x7f | has_imm8 << 7)) {
23362306a36Sopenharmony_ci			pr_err("immediate too large\n");
23462306a36Sopenharmony_ci			return 0;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		*i8 = val & 0x80;
23762306a36Sopenharmony_ci		return RE_REG_IMM_encode(val & 0x7f);
23862306a36Sopenharmony_ci	case NN_REG_NONE:
23962306a36Sopenharmony_ci		return is_dst ? RE_REG_NO_DST : REG_NONE;
24062306a36Sopenharmony_ci	case NN_REG_NNR:
24162306a36Sopenharmony_ci		pr_err("NNRs used with restricted encoding\n");
24262306a36Sopenharmony_ci		return 0;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	pr_err("unrecognized reg encoding\n");
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,
25062306a36Sopenharmony_ci			struct nfp_insn_re_regs *reg, bool has_imm8)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	memset(reg, 0, sizeof(*reg));
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Decode destination */
25562306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_IMM)
25662306a36Sopenharmony_ci		return -EFAULT;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_GPR_B)
25962306a36Sopenharmony_ci		reg->dst_ab = ALU_DST_B;
26062306a36Sopenharmony_ci	if (swreg_type(dst) == NN_REG_GPR_BOTH)
26162306a36Sopenharmony_ci		reg->wr_both = true;
26262306a36Sopenharmony_ci	reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Decode source operands */
26562306a36Sopenharmony_ci	if (swreg_type(lreg) == swreg_type(rreg) &&
26662306a36Sopenharmony_ci	    swreg_type(lreg) != NN_REG_NONE)
26762306a36Sopenharmony_ci		return -EFAULT;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (swreg_type(lreg) == NN_REG_GPR_B ||
27062306a36Sopenharmony_ci	    swreg_type(rreg) == NN_REG_GPR_A) {
27162306a36Sopenharmony_ci		reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
27262306a36Sopenharmony_ci		reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
27362306a36Sopenharmony_ci		reg->swap = true;
27462306a36Sopenharmony_ci	} else {
27562306a36Sopenharmony_ci		reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
27662306a36Sopenharmony_ci		reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	reg->dst_lmextn = swreg_lmextn(dst);
28062306a36Sopenharmony_ci	reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci#define NFP_USTORE_ECC_POLY_WORDS		7
28662306a36Sopenharmony_ci#define NFP_USTORE_OP_BITS			45
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = {
28962306a36Sopenharmony_ci	0x0ff800007fffULL,
29062306a36Sopenharmony_ci	0x11f801ff801fULL,
29162306a36Sopenharmony_ci	0x1e387e0781e1ULL,
29262306a36Sopenharmony_ci	0x17cb8e388e22ULL,
29362306a36Sopenharmony_ci	0x1af5b2c93244ULL,
29462306a36Sopenharmony_ci	0x1f56d5525488ULL,
29562306a36Sopenharmony_ci	0x0daf69a46910ULL,
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic bool parity(u64 value)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	return hweight64(value) & 1;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciint nfp_ustore_check_valid_no_ecc(u64 insn)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0))
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciu64 nfp_ustore_calc_ecc_insn(u64 insn)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	u8 ecc = 0;
31462306a36Sopenharmony_ci	int i;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++)
31762306a36Sopenharmony_ci		ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return insn | (u64)ecc << NFP_USTORE_OP_BITS;
32062306a36Sopenharmony_ci}
321