162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitfield.h>
562306a36Sopenharmony_ci#include <linux/bitops.h>
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/string.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "prestera_dsa.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define PRESTERA_DSA_W0_CMD		GENMASK(31, 30)
1262306a36Sopenharmony_ci#define PRESTERA_DSA_W0_IS_TAGGED	BIT(29)
1362306a36Sopenharmony_ci#define PRESTERA_DSA_W0_DEV_NUM		GENMASK(28, 24)
1462306a36Sopenharmony_ci#define PRESTERA_DSA_W0_PORT_NUM	GENMASK(23, 19)
1562306a36Sopenharmony_ci#define PRESTERA_DSA_W0_VPT		GENMASK(15, 13)
1662306a36Sopenharmony_ci#define PRESTERA_DSA_W0_EXT_BIT		BIT(12)
1762306a36Sopenharmony_ci#define PRESTERA_DSA_W0_VID		GENMASK(11, 0)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define PRESTERA_DSA_W1_EXT_BIT		BIT(31)
2062306a36Sopenharmony_ci#define PRESTERA_DSA_W1_CFI_BIT		BIT(30)
2162306a36Sopenharmony_ci#define PRESTERA_DSA_W1_PORT_NUM	GENMASK(11, 10)
2262306a36Sopenharmony_ci#define PRESTERA_DSA_W1_MASK_CPU_CODE	GENMASK(7, 0)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PRESTERA_DSA_W2_EXT_BIT		BIT(31)
2562306a36Sopenharmony_ci#define PRESTERA_DSA_W2_PORT_NUM	BIT(20)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PRESTERA_DSA_W3_VID		GENMASK(30, 27)
2862306a36Sopenharmony_ci#define PRESTERA_DSA_W3_DST_EPORT	GENMASK(23, 7)
2962306a36Sopenharmony_ci#define PRESTERA_DSA_W3_DEV_NUM		GENMASK(6, 0)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define PRESTERA_DSA_VID		GENMASK(15, 12)
3262306a36Sopenharmony_ci#define PRESTERA_DSA_DEV_NUM		GENMASK(11, 5)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciint prestera_dsa_parse(struct prestera_dsa *dsa, const u8 *dsa_buf)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	__be32 *dsa_words = (__be32 *)dsa_buf;
3762306a36Sopenharmony_ci	enum prestera_dsa_cmd cmd;
3862306a36Sopenharmony_ci	u32 words[4];
3962306a36Sopenharmony_ci	u32 field;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	words[0] = ntohl(dsa_words[0]);
4262306a36Sopenharmony_ci	words[1] = ntohl(dsa_words[1]);
4362306a36Sopenharmony_ci	words[2] = ntohl(dsa_words[2]);
4462306a36Sopenharmony_ci	words[3] = ntohl(dsa_words[3]);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* set the common parameters */
4762306a36Sopenharmony_ci	cmd = (enum prestera_dsa_cmd)FIELD_GET(PRESTERA_DSA_W0_CMD, words[0]);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* only to CPU is supported */
5062306a36Sopenharmony_ci	if (unlikely(cmd != PRESTERA_DSA_CMD_TO_CPU))
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (FIELD_GET(PRESTERA_DSA_W0_EXT_BIT, words[0]) == 0)
5462306a36Sopenharmony_ci		return -EINVAL;
5562306a36Sopenharmony_ci	if (FIELD_GET(PRESTERA_DSA_W1_EXT_BIT, words[1]) == 0)
5662306a36Sopenharmony_ci		return -EINVAL;
5762306a36Sopenharmony_ci	if (FIELD_GET(PRESTERA_DSA_W2_EXT_BIT, words[2]) == 0)
5862306a36Sopenharmony_ci		return -EINVAL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	field = FIELD_GET(PRESTERA_DSA_W3_VID, words[3]);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	dsa->vlan.is_tagged = FIELD_GET(PRESTERA_DSA_W0_IS_TAGGED, words[0]);
6362306a36Sopenharmony_ci	dsa->vlan.cfi_bit = FIELD_GET(PRESTERA_DSA_W1_CFI_BIT, words[1]);
6462306a36Sopenharmony_ci	dsa->vlan.vpt = FIELD_GET(PRESTERA_DSA_W0_VPT, words[0]);
6562306a36Sopenharmony_ci	dsa->vlan.vid = FIELD_GET(PRESTERA_DSA_W0_VID, words[0]);
6662306a36Sopenharmony_ci	dsa->vlan.vid &= ~PRESTERA_DSA_VID;
6762306a36Sopenharmony_ci	dsa->vlan.vid |= FIELD_PREP(PRESTERA_DSA_VID, field);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	field = FIELD_GET(PRESTERA_DSA_W3_DEV_NUM, words[3]);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	dsa->hw_dev_num = FIELD_GET(PRESTERA_DSA_W0_DEV_NUM, words[0]);
7262306a36Sopenharmony_ci	dsa->hw_dev_num |= FIELD_PREP(PRESTERA_DSA_DEV_NUM, field);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	dsa->port_num = (FIELD_GET(PRESTERA_DSA_W0_PORT_NUM, words[0]) << 0) |
7562306a36Sopenharmony_ci			(FIELD_GET(PRESTERA_DSA_W1_PORT_NUM, words[1]) << 5) |
7662306a36Sopenharmony_ci			(FIELD_GET(PRESTERA_DSA_W2_PORT_NUM, words[2]) << 7);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	dsa->cpu_code = FIELD_GET(PRESTERA_DSA_W1_MASK_CPU_CODE, words[1]);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciint prestera_dsa_build(const struct prestera_dsa *dsa, u8 *dsa_buf)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	__be32 *dsa_words = (__be32 *)dsa_buf;
8662306a36Sopenharmony_ci	u32 dev_num = dsa->hw_dev_num;
8762306a36Sopenharmony_ci	u32 words[4] = { 0 };
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	words[0] |= FIELD_PREP(PRESTERA_DSA_W0_CMD, PRESTERA_DSA_CMD_FROM_CPU);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	words[0] |= FIELD_PREP(PRESTERA_DSA_W0_DEV_NUM, dev_num);
9262306a36Sopenharmony_ci	dev_num = FIELD_GET(PRESTERA_DSA_DEV_NUM, dev_num);
9362306a36Sopenharmony_ci	words[3] |= FIELD_PREP(PRESTERA_DSA_W3_DEV_NUM, dev_num);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	words[3] |= FIELD_PREP(PRESTERA_DSA_W3_DST_EPORT, dsa->port_num);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	words[0] |= FIELD_PREP(PRESTERA_DSA_W0_EXT_BIT, 1);
9862306a36Sopenharmony_ci	words[1] |= FIELD_PREP(PRESTERA_DSA_W1_EXT_BIT, 1);
9962306a36Sopenharmony_ci	words[2] |= FIELD_PREP(PRESTERA_DSA_W2_EXT_BIT, 1);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	dsa_words[0] = htonl(words[0]);
10262306a36Sopenharmony_ci	dsa_words[1] = htonl(words[1]);
10362306a36Sopenharmony_ci	dsa_words[2] = htonl(words[2]);
10462306a36Sopenharmony_ci	dsa_words[3] = htonl(words[3]);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
108