Subject: Re: [RFC next-next v2 1/5] net: marvell: prestera: Add driver for Prestera family ASIC devices

From: Vadym Kochan <[email-masked]>
Date: Mon, 11 May 2020 22:24:22 +0300

On Mon, May 11, 2020 at 02:57:23PM +0200, Jiri Pirko wrote:
> [...]
> 
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_dsa.c b/drivers/net/ethernet/marvell/prestera/prestera_dsa.c
> >new file mode 100644
> >index 000000000000..c4f7d9f6edcb
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_dsa.c
> >@@ -0,0 +1,134 @@
> >+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
> >+
> >+#include "prestera_dsa.h"
> >+
> >+#include <linux/string.h>
> >+#include <linux/bitops.h>
> >+#include <linux/bitfield.h>
> >+#include <linux/errno.h>
> >+
> >+#define W0_MASK_IS_TAGGED	BIT(29)
> >+
> >+/* TrgDev[4:0] = {Word0[28:24]} */
> >+#define W0_MASK_HW_DEV_NUM	GENMASK(28, 24)
> >+
> >+/* SrcPort/TrgPort extended to 8b
> >+ * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> >+ */
> >+#define W0_MASK_IFACE_PORT_NUM	GENMASK(23, 19)
> >+
> >+/* bits 30:31 - TagCommand 1 = FROM_CPU */
> >+#define W0_MASK_DSA_CMD		GENMASK(31, 30)
> >+
> >+/* bits 13:15 -- UP */
> >+#define W0_MASK_VPT		GENMASK(15, 13)
> >+
> >+#define W0_MASK_EXT_BIT		BIT(12)
> >+
> >+/* bits 0:11 -- VID */
> >+#define W0_MASK_VID		GENMASK(11, 0)
> >+
> >+/* SrcPort/TrgPort extended to 8b
> >+ * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> >+ */
> >+#define W1_MASK_IFACE_PORT_NUM	GENMASK(11, 10)
> >+
> >+#define W1_MASK_EXT_BIT		BIT(31)
> >+#define W1_MASK_CFI_BIT		BIT(30)
> >+
> >+/* SrcPort/TrgPort extended to 8b
> >+ * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> >+ */
> >+#define W2_MASK_IFACE_PORT_NUM	BIT(20)
> >+
> >+#define W2_MASK_EXT_BIT		BIT(31)
> >+
> >+/* trgHwDev and trgPort
> >+ * TrgDev[11:5] = {Word3[6:0]}
> >+ */
> >+#define W3_MASK_HW_DEV_NUM	GENMASK(6, 0)
> >+
> >+/* VID 16b [15:0] = {Word3[30:27], Word0[11:0]} */
> >+#define W3_MASK_VID		GENMASK(30, 27)
> >+
> >+/* TRGePort[16:0] = {Word3[23:7]} */
> >+#define W3_MASK_DST_EPORT	GENMASK(23, 7)
> >+
> >+#define DEV_NUM_MASK		GENMASK(11, 5)
> >+#define VID_MASK		GENMASK(15, 12)
> 
> Looks like you forgot to add the "prestera" prefix here.
> 
> 
> >+
> >+int prestera_dsa_parse(struct prestera_dsa *dsa, const u8 *dsa_buf)
> >+{
> >+	u32 *dsa_words = (u32 *)dsa_buf;
> >+	enum prestera_dsa_cmd cmd;
> >+	u32 words[4] = { 0 };
> >+	u32 field;
> >+
> >+	words[0] = ntohl((__force __be32)dsa_words[0]);
> >+	words[1] = ntohl((__force __be32)dsa_words[1]);
> >+	words[2] = ntohl((__force __be32)dsa_words[2]);
> >+	words[3] = ntohl((__force __be32)dsa_words[3]);
> >+
> >+	/* set the common parameters */
> >+	cmd = (enum prestera_dsa_cmd)FIELD_GET(W0_MASK_DSA_CMD, words[0]);
> >+
> >+	/* only to CPU is supported */
> >+	if (unlikely(cmd != PRESTERA_DSA_CMD_TO_CPU_E))
> >+		return -EINVAL;
> >+
> >+	if (FIELD_GET(W0_MASK_EXT_BIT, words[0]) == 0)
> >+		return -EINVAL;
> >+	if (FIELD_GET(W1_MASK_EXT_BIT, words[1]) == 0)
> >+		return -EINVAL;
> >+	if (FIELD_GET(W2_MASK_EXT_BIT, words[2]) == 0)
> >+		return -EINVAL;
> >+
> >+	field = FIELD_GET(W3_MASK_VID, words[3]);
> >+
> >+	dsa->vlan.is_tagged = (bool)FIELD_GET(W0_MASK_IS_TAGGED, words[0]);
> >+	dsa->vlan.cfi_bit = (u8)FIELD_GET(W1_MASK_CFI_BIT, words[1]);
> >+	dsa->vlan.vpt = (u8)FIELD_GET(W0_MASK_VPT, words[0]);
> >+	dsa->vlan.vid = (u16)FIELD_GET(W0_MASK_VID, words[0]);
> >+	dsa->vlan.vid &= ~VID_MASK;
> >+	dsa->vlan.vid |= FIELD_PREP(VID_MASK, field);
> >+
> >+	field = FIELD_GET(W3_MASK_HW_DEV_NUM, words[3]);
> >+
> >+	dsa->hw_dev_num = FIELD_GET(W0_MASK_HW_DEV_NUM, words[0]);
> >+	dsa->hw_dev_num &= W3_MASK_HW_DEV_NUM;
> >+	dsa->hw_dev_num |= FIELD_PREP(DEV_NUM_MASK, field);
> >+
> >+	dsa->port_num = (FIELD_GET(W0_MASK_IFACE_PORT_NUM, words[0]) << 0) |
> >+			(FIELD_GET(W1_MASK_IFACE_PORT_NUM, words[1]) << 5) |
> >+			(FIELD_GET(W2_MASK_IFACE_PORT_NUM, words[2]) << 7);
> >+	return 0;
> >+}
> >+
> >+int prestera_dsa_build(const struct prestera_dsa *dsa, u8 *dsa_buf)
> >+{
> >+	__be32 *dsa_words = (__be32 *)dsa_buf;
> >+	u32 words[4] = { 0 };
> >+
> >+	if (dsa->hw_dev_num >= BIT(12))
> >+		return -EINVAL;
> >+	if (dsa->port_num >= BIT(17))
> >+		return -EINVAL;
> >+
> >+	words[0] |= FIELD_PREP(W0_MASK_DSA_CMD, PRESTERA_DSA_CMD_FROM_CPU_E);
> >+
> >+	words[0] |= FIELD_PREP(W0_MASK_HW_DEV_NUM, dsa->hw_dev_num);
> >+	words[3] |= FIELD_PREP(W3_MASK_HW_DEV_NUM, (dsa->hw_dev_num >> 5));
> >+	words[3] |= FIELD_PREP(W3_MASK_DST_EPORT, dsa->port_num);
> >+
> >+	words[0] |= FIELD_PREP(W0_MASK_EXT_BIT, 1);
> >+	words[1] |= FIELD_PREP(W1_MASK_EXT_BIT, 1);
> >+	words[2] |= FIELD_PREP(W2_MASK_EXT_BIT, 1);
> >+
> >+	dsa_words[0] = htonl(words[0]);
> >+	dsa_words[1] = htonl(words[1]);
> >+	dsa_words[2] = htonl(words[2]);
> >+	dsa_words[3] = htonl(words[3]);
> >+
> >+	return 0;
> >+}
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_dsa.h b/drivers/net/ethernet/marvell/prestera/prestera_dsa.h
> >new file mode 100644
> >index 000000000000..34cb260f1a74
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_dsa.h
> >@@ -0,0 +1,37 @@
> >+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+ *
> >+ * Copyright (c) 2020 Marvell International Ltd. All rights reserved.
> >+ *
> >+ */
> >+#ifndef __PRESTERA_DSA_H_
> >+#define __PRESTERA_DSA_H_
> >+
> >+#include <linux/types.h>
> >+
> >+#define PRESTERA_DSA_HLEN	16
> >+
> >+enum prestera_dsa_cmd {
> >+	/* DSA command is "To CPU" */
> >+	PRESTERA_DSA_CMD_TO_CPU_E = 0,
> >+
> >+	/* DSA command is "FROM CPU" */
> >+	PRESTERA_DSA_CMD_FROM_CPU_E,
> >+};
> >+
> >+struct prestera_dsa_vlan {
> >+	u16 vid;
> >+	u8 vpt;
> >+	u8 cfi_bit;
> >+	bool is_tagged;
> >+};
> >+
> >+struct prestera_dsa {
> >+	struct prestera_dsa_vlan vlan;
> >+	u32 hw_dev_num;
> >+	u32 port_num;
> >+};
> >+
> >+int prestera_dsa_parse(struct prestera_dsa *dsa, const u8 *dsa_buf);
> >+int prestera_dsa_build(const struct prestera_dsa *dsa, u8 *dsa_buf);
> >+
> >+#endif /* _PRESTERA_DSA_H_ */
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c
> >new file mode 100644
> >index 000000000000..b4626cf288b6
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c
> >@@ -0,0 +1,614 @@
> >+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
> >+
> >+#include <linux/etherdevice.h>
> >+#include <linux/ethtool.h>
> >+#include <linux/netdevice.h>
> >+#include <linux/list.h>
> >+
> >+#include "prestera.h"
> >+#include "prestera_hw.h"
> >+
> >+#define PRESTERA_SWITCH_INIT_TIMEOUT 30000000	/* 30sec */
> >+#define PRESTERA_MIN_MTU 64
> >+
> >+enum prestera_cmd_type_t {
> >+	PRESTERA_CMD_TYPE_SWITCH_INIT = 0x1,
> >+	PRESTERA_CMD_TYPE_SWITCH_ATTR_SET = 0x2,
> >+
> >+	PRESTERA_CMD_TYPE_PORT_ATTR_SET = 0x100,
> >+	PRESTERA_CMD_TYPE_PORT_ATTR_GET = 0x101,
> >+	PRESTERA_CMD_TYPE_PORT_INFO_GET = 0x110,
> >+
> >+	PRESTERA_CMD_TYPE_RXTX_INIT = 0x800,
> >+	PRESTERA_CMD_TYPE_RXTX_PORT_INIT = 0x801,
> >+
> >+	PRESTERA_CMD_TYPE_ACK = 0x10000,
> >+	PRESTERA_CMD_TYPE_MAX
> >+};
> >+
> >+enum {
> >+	PRESTERA_CMD_PORT_ATTR_ADMIN_STATE = 1,
> >+	PRESTERA_CMD_PORT_ATTR_MTU = 3,
> >+	PRESTERA_CMD_PORT_ATTR_MAC = 4,
> >+	PRESTERA_CMD_PORT_ATTR_CAPABILITY = 9,
> >+	PRESTERA_CMD_PORT_ATTR_AUTONEG = 15,
> >+	PRESTERA_CMD_PORT_ATTR_STATS = 17,
> >+};
> >+
> >+enum {
> >+	PRESTERA_CMD_SWITCH_ATTR_MAC = 1,
> >+};
> >+
> >+enum {
> >+	PRESTERA_CMD_ACK_OK,
> >+	PRESTERA_CMD_ACK_FAILED,
> >+
> >+	PRESTERA_CMD_ACK_MAX
> >+};
> >+
> >+enum {
> >+	PRESTERA_PORT_GOOD_OCTETS_RCV_CNT,
> >+	PRESTERA_PORT_BAD_OCTETS_RCV_CNT,
> >+	PRESTERA_PORT_MAC_TRANSMIT_ERR_CNT,
> >+	PRESTERA_PORT_BRDC_PKTS_RCV_CNT,
> >+	PRESTERA_PORT_MC_PKTS_RCV_CNT,
> >+	PRESTERA_PORT_PKTS_64L_CNT,
> >+	PRESTERA_PORT_PKTS_65TO127L_CNT,
> >+	PRESTERA_PORT_PKTS_128TO255L_CNT,
> >+	PRESTERA_PORT_PKTS_256TO511L_CNT,
> >+	PRESTERA_PORT_PKTS_512TO1023L_CNT,
> >+	PRESTERA_PORT_PKTS_1024TOMAXL_CNT,
> >+	PRESTERA_PORT_EXCESSIVE_COLLISIONS_CNT,
> >+	PRESTERA_PORT_MC_PKTS_SENT_CNT,
> >+	PRESTERA_PORT_BRDC_PKTS_SENT_CNT,
> >+	PRESTERA_PORT_FC_SENT_CNT,
> >+	PRESTERA_PORT_GOOD_FC_RCV_CNT,
> >+	PRESTERA_PORT_DROP_EVENTS_CNT,
> >+	PRESTERA_PORT_UNDERSIZE_PKTS_CNT,
> >+	PRESTERA_PORT_FRAGMENTS_PKTS_CNT,
> >+	PRESTERA_PORT_OVERSIZE_PKTS_CNT,
> >+	PRESTERA_PORT_JABBER_PKTS_CNT,
> >+	PRESTERA_PORT_MAC_RCV_ERROR_CNT,
> >+	PRESTERA_PORT_BAD_CRC_CNT,
> >+	PRESTERA_PORT_COLLISIONS_CNT,
> >+	PRESTERA_PORT_LATE_COLLISIONS_CNT,
> >+	PRESTERA_PORT_GOOD_UC_PKTS_RCV_CNT,
> >+	PRESTERA_PORT_GOOD_UC_PKTS_SENT_CNT,
> >+	PRESTERA_PORT_MULTIPLE_PKTS_SENT_CNT,
> >+	PRESTERA_PORT_DEFERRED_PKTS_SENT_CNT,
> >+	PRESTERA_PORT_PKTS_1024TO1518L_CNT,
> >+	PRESTERA_PORT_PKTS_1519TOMAXL_CNT,
> >+	PRESTERA_PORT_GOOD_OCTETS_SENT_CNT,
> >+
> >+	PRESTERA_PORT_CNT_MAX,
> >+};
> >+
> >+struct prestera_fw_event_handler {
> >+	struct list_head list;
> >+	enum prestera_event_type type;
> >+	prestera_event_cb_t func;
> >+	void *arg;
> >+};
> >+
> >+struct prestera_msg_cmd {
> >+	u32 type;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_ret {
> >+	struct prestera_msg_cmd cmd;
> >+	u32 status;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_common_req {
> >+	struct prestera_msg_cmd cmd;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_common_resp {
> >+	struct prestera_msg_ret ret;
> >+} __packed __aligned(4);
> >+
> >+union prestera_msg_switch_param {
> >+	u8 mac[ETH_ALEN];
> >+};
> >+
> >+struct prestera_msg_switch_attr_req {
> >+	struct prestera_msg_cmd cmd;
> >+	u32 attr;
> >+	union prestera_msg_switch_param param;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_switch_init_resp {
> >+	struct prestera_msg_ret ret;
> >+	u32 port_count;
> >+	u32 mtu_max;
> >+	u8  switch_id;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_port_autoneg_param {
> >+	u64 link_mode;
> >+	u8  enable;
> >+	u8  fec;
> >+};
> >+
> >+struct prestera_msg_port_cap_param {
> >+	u64 link_mode;
> >+	u8  type;
> >+	u8  fec;
> >+	u8  transceiver;
> >+};
> >+
> >+union prestera_msg_port_param {
> >+	u8  admin_state;
> >+	u8  oper_state;
> >+	u32 mtu;
> >+	u8  mac[ETH_ALEN];
> >+	struct prestera_msg_port_autoneg_param autoneg;
> >+	struct prestera_msg_port_cap_param cap;
> >+};
> >+
> >+struct prestera_msg_port_attr_req {
> >+	struct prestera_msg_cmd cmd;
> >+	u32 attr;
> >+	u32 port;
> >+	u32 dev;
> >+	union prestera_msg_port_param param;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_port_attr_resp {
> >+	struct prestera_msg_ret ret;
> >+	union prestera_msg_port_param param;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_port_stats_resp {
> >+	struct prestera_msg_ret ret;
> >+	u64 stats[PRESTERA_PORT_CNT_MAX];
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_port_info_req {
> >+	struct prestera_msg_cmd cmd;
> >+	u32 port;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_port_info_resp {
> >+	struct prestera_msg_ret ret;
> >+	u32 hw_id;
> >+	u32 dev_id;
> >+	u16 fp_id;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_rxtx_req {
> >+	struct prestera_msg_cmd cmd;
> >+	u8 use_sdma;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_rxtx_resp {
> >+	struct prestera_msg_ret ret;
> >+	u32 map_addr;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_rxtx_port_req {
> >+	struct prestera_msg_cmd cmd;
> >+	u32 port;
> >+	u32 dev;
> >+} __packed __aligned(4);
> >+
> >+struct prestera_msg_event {
> >+	u16 type;
> >+	u16 id;
> >+} __packed __aligned(4);
> >+
> >+union prestera_msg_event_port_param {
> >+	u32 oper_state;
> >+};
> >+
> >+struct prestera_msg_event_port {
> >+	struct prestera_msg_event id;
> >+	u32 port_id;
> >+	union prestera_msg_event_port_param param;
> >+} __packed __aligned(4);
> >+
> >+static int __prestera_cmd_ret(struct prestera_switch *sw,
> >+			      enum prestera_cmd_type_t type,
> >+			      struct prestera_msg_cmd *cmd, size_t clen,
> >+			      struct prestera_msg_ret *ret, size_t rlen,
> >+			      int wait)
> >+{
> >+	struct prestera_device *dev = sw->dev;
> >+	int err;
> >+
> >+	cmd->type = type;
> >+
> >+	err = dev->send_req(dev, (u8 *)cmd, clen, (u8 *)ret, rlen, wait);
> >+	if (err)
> >+		return err;
> >+
> >+	if (ret->cmd.type != PRESTERA_CMD_TYPE_ACK)
> >+		return -EBADE;
> >+	if (ret->status != PRESTERA_CMD_ACK_OK)
> >+		return -EINVAL;
> >+
> >+	return 0;
> >+}
> >+
> >+static int prestera_cmd_ret(struct prestera_switch *sw,
> >+			    enum prestera_cmd_type_t type,
> >+			    struct prestera_msg_cmd *cmd, size_t clen,
> >+			    struct prestera_msg_ret *ret, size_t rlen)
> >+{
> >+	return __prestera_cmd_ret(sw, type, cmd, clen, ret, rlen, 0);
> >+}
> >+
> >+static int prestera_cmd_ret_wait(struct prestera_switch *sw,
> >+				 enum prestera_cmd_type_t type,
> >+				 struct prestera_msg_cmd *cmd, size_t clen,
> >+				 struct prestera_msg_ret *ret, size_t rlen,
> >+				 int wait)
> >+{
> >+	return __prestera_cmd_ret(sw, type, cmd, clen, ret, rlen, wait);
> >+}
> >+
> >+static int prestera_cmd(struct prestera_switch *sw,
> >+			enum prestera_cmd_type_t type,
> >+			struct prestera_msg_cmd *cmd, size_t clen)
> >+{
> >+	struct prestera_msg_common_resp resp;
> >+
> >+	return prestera_cmd_ret(sw, type, cmd, clen, &resp.ret, sizeof(resp));
> >+}
> >+
> >+static int prestera_fw_parse_port_evt(u8 *msg, struct prestera_event *evt)
> >+{
> >+	struct prestera_msg_event_port *hw_evt;
> >+
> >+	hw_evt = (struct prestera_msg_event_port *)msg;
> >+
> >+	evt->port_evt.port_id = hw_evt->port_id;
> >+
> >+	if (evt->id == PRESTERA_PORT_EVENT_STATE_CHANGED)
> >+		evt->port_evt.data.oper_state = hw_evt->param.oper_state;
> >+	else
> >+		return -EINVAL;
> >+
> >+	return 0;
> >+}
> >+
> >+static struct prestera_fw_evt_parser {
> >+	int (*func)(u8 *msg, struct prestera_event *evt);
> >+} fw_event_parsers[PRESTERA_EVENT_TYPE_MAX] = {
> >+	[PRESTERA_EVENT_TYPE_PORT] = {.func = prestera_fw_parse_port_evt},
> >+};
> >+
> >+static struct prestera_fw_event_handler *
> >+__find_event_handler(const struct prestera_switch *sw,
> >+		     enum prestera_event_type type)
> >+{
> >+	struct prestera_fw_event_handler *eh;
> >+
> >+	list_for_each_entry_rcu(eh, &sw->event_handlers, list) {
> >+		if (eh->type == type)
> >+			return eh;
> >+	}
> >+
> >+	return NULL;
> >+}
> >+
> >+static int prestera_find_event_handler(const struct prestera_switch *sw,
> >+				       enum prestera_event_type type,
> >+				       struct prestera_fw_event_handler *eh)
> >+{
> >+	struct prestera_fw_event_handler *tmp;
> >+	int err = 0;
> >+
> >+	rcu_read_lock();
> >+	tmp = __find_event_handler(sw, type);
> >+	if (tmp)
> >+		*eh = *tmp;
> >+	else
> >+		err = -EEXIST;
> >+	rcu_read_unlock();
> >+
> >+	return err;
> >+}
> >+
> >+static int prestera_evt_recv(struct prestera_device *dev, u8 *buf, size_t size)
> >+{
> >+	struct prestera_msg_event *msg = (struct prestera_msg_event *)buf;
> >+	struct prestera_switch *sw = dev->priv;
> >+	struct prestera_fw_event_handler eh;
> >+	struct prestera_event evt;
> >+	int err;
> >+
> >+	if (msg->type >= PRESTERA_EVENT_TYPE_MAX)
> >+		return -EINVAL;
> >+
> >+	err = prestera_find_event_handler(sw, msg->type, &eh);
> >+
> >+	if (err || !fw_event_parsers[msg->type].func)
> >+		return 0;
> >+
> >+	evt.id = msg->id;
> >+
> >+	err = fw_event_parsers[msg->type].func(buf, &evt);
> >+	if (!err)
> >+		eh.func(sw, &evt, eh.arg);
> >+
> >+	return err;
> >+}
> >+
> >+static void prestera_pkt_recv(struct prestera_device *dev)
> >+{
> >+	struct prestera_switch *sw = dev->priv;
> >+	struct prestera_fw_event_handler eh;
> >+	struct prestera_event ev;
> >+	int err;
> >+
> >+	ev.id = PRESTERA_RXTX_EVENT_RCV_PKT;
> >+
> >+	err = prestera_find_event_handler(sw, PRESTERA_EVENT_TYPE_RXTX, &eh);
> >+	if (err)
> >+		return;
> >+
> >+	eh.func(sw, &ev, eh.arg);
> >+}
> >+
> >+int prestera_hw_port_info_get(const struct prestera_port *port,
> >+			      u16 *fp_id, u32 *hw_id, u32 *dev_id)
> >+{
> >+	struct prestera_msg_port_info_resp resp;
> >+	struct prestera_msg_port_info_req req = {
> >+		.port = port->id
> >+	};
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_INFO_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	*hw_id = resp.hw_id;
> >+	*dev_id = resp.dev_id;
> >+	*fp_id = resp.fp_id;
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_switch_mac_set(struct prestera_switch *sw, char *mac)
> >+{
> >+	struct prestera_msg_switch_attr_req req = {
> >+		.attr = PRESTERA_CMD_SWITCH_ATTR_MAC,
> >+	};
> >+
> >+	memcpy(req.param.mac, mac, sizeof(req.param.mac));
> >+
> >+	return prestera_cmd(sw, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_switch_init(struct prestera_switch *sw)
> >+{
> >+	struct prestera_msg_switch_init_resp resp;
> >+	struct prestera_msg_common_req req;
> >+	int err;
> >+
> >+	INIT_LIST_HEAD(&sw->event_handlers);
> >+
> >+	err = prestera_cmd_ret_wait(sw, PRESTERA_CMD_TYPE_SWITCH_INIT,
> >+				    &req.cmd, sizeof(req),
> >+				    &resp.ret, sizeof(resp),
> >+				    PRESTERA_SWITCH_INIT_TIMEOUT);
> >+	if (err)
> >+		return err;
> >+
> >+	sw->id = resp.switch_id;
> >+	sw->port_count = resp.port_count;
> >+	sw->mtu_min = PRESTERA_MIN_MTU;
> >+	sw->mtu_max = resp.mtu_max;
> >+	sw->dev->recv_msg = prestera_evt_recv;
> >+	sw->dev->recv_pkt = prestera_pkt_recv;
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_port_state_set(const struct prestera_port *port,
> >+			       bool admin_state)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_ADMIN_STATE,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.admin_state = admin_state}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_MTU,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.mtu = mtu}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_mac_set(const struct prestera_port *port, char *mac)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_MAC,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	memcpy(&req.param.mac, mac, sizeof(req.param.mac));
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_cap_get(const struct prestera_port *port,
> >+			     struct prestera_port_caps *caps)
> >+{
> >+	struct prestera_msg_port_attr_resp resp;
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_CAPABILITY,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	caps->supp_link_modes = resp.param.cap.link_mode;
> >+	caps->supp_fec = resp.param.cap.fec;
> >+	caps->type = resp.param.cap.type;
> >+	caps->transceiver = resp.param.cap.transceiver;
> >+
> >+	return err;
> >+}
> >+
> >+int prestera_hw_port_autoneg_set(const struct prestera_port *port,
> >+				 bool autoneg, u64 link_modes, u8 fec)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_AUTONEG,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.autoneg = {.link_mode = link_modes,
> >+				      .enable = autoneg,
> >+				      .fec = fec}
> >+		}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_stats_get(const struct prestera_port *port,
> >+			       struct prestera_port_stats *st)
> >+{
> >+	struct prestera_msg_port_stats_resp resp;
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_STATS,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	u64 *hw = resp.stats;
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	st->good_octets_received = hw[PRESTERA_PORT_GOOD_OCTETS_RCV_CNT];
> >+	st->bad_octets_received = hw[PRESTERA_PORT_BAD_OCTETS_RCV_CNT];
> >+	st->mac_trans_error = hw[PRESTERA_PORT_MAC_TRANSMIT_ERR_CNT];
> >+	st->broadcast_frames_received = hw[PRESTERA_PORT_BRDC_PKTS_RCV_CNT];
> >+	st->multicast_frames_received = hw[PRESTERA_PORT_MC_PKTS_RCV_CNT];
> >+	st->frames_64_octets = hw[PRESTERA_PORT_PKTS_64L_CNT];
> >+	st->frames_65_to_127_octets = hw[PRESTERA_PORT_PKTS_65TO127L_CNT];
> >+	st->frames_128_to_255_octets = hw[PRESTERA_PORT_PKTS_128TO255L_CNT];
> >+	st->frames_256_to_511_octets = hw[PRESTERA_PORT_PKTS_256TO511L_CNT];
> >+	st->frames_512_to_1023_octets = hw[PRESTERA_PORT_PKTS_512TO1023L_CNT];
> >+	st->frames_1024_to_max_octets = hw[PRESTERA_PORT_PKTS_1024TOMAXL_CNT];
> >+	st->excessive_collision = hw[PRESTERA_PORT_EXCESSIVE_COLLISIONS_CNT];
> >+	st->multicast_frames_sent = hw[PRESTERA_PORT_MC_PKTS_SENT_CNT];
> >+	st->broadcast_frames_sent = hw[PRESTERA_PORT_BRDC_PKTS_SENT_CNT];
> >+	st->fc_sent = hw[PRESTERA_PORT_FC_SENT_CNT];
> >+	st->fc_received = hw[PRESTERA_PORT_GOOD_FC_RCV_CNT];
> >+	st->buffer_overrun = hw[PRESTERA_PORT_DROP_EVENTS_CNT];
> >+	st->undersize = hw[PRESTERA_PORT_UNDERSIZE_PKTS_CNT];
> >+	st->fragments = hw[PRESTERA_PORT_FRAGMENTS_PKTS_CNT];
> >+	st->oversize = hw[PRESTERA_PORT_OVERSIZE_PKTS_CNT];
> >+	st->jabber = hw[PRESTERA_PORT_JABBER_PKTS_CNT];
> >+	st->rx_error_frame_received = hw[PRESTERA_PORT_MAC_RCV_ERROR_CNT];
> >+	st->bad_crc = hw[PRESTERA_PORT_BAD_CRC_CNT];
> >+	st->collisions = hw[PRESTERA_PORT_COLLISIONS_CNT];
> >+	st->late_collision = hw[PRESTERA_PORT_LATE_COLLISIONS_CNT];
> >+	st->unicast_frames_received = hw[PRESTERA_PORT_GOOD_UC_PKTS_RCV_CNT];
> >+	st->unicast_frames_sent = hw[PRESTERA_PORT_GOOD_UC_PKTS_SENT_CNT];
> >+	st->sent_multiple = hw[PRESTERA_PORT_MULTIPLE_PKTS_SENT_CNT];
> >+	st->sent_deferred = hw[PRESTERA_PORT_DEFERRED_PKTS_SENT_CNT];
> >+	st->frames_1024_to_1518_octets = hw[PRESTERA_PORT_PKTS_1024TO1518L_CNT];
> >+	st->frames_1519_to_max_octets = hw[PRESTERA_PORT_PKTS_1519TOMAXL_CNT];
> >+	st->good_octets_sent = hw[PRESTERA_PORT_GOOD_OCTETS_SENT_CNT];
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_rxtx_init(struct prestera_switch *sw,
> >+			  struct prestera_rxtx_params *params)
> >+{
> >+	struct prestera_msg_rxtx_resp resp;
> >+	struct prestera_msg_rxtx_req req;
> >+	int err;
> >+
> >+	req.use_sdma = params->use_sdma;
> >+
> >+	err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_RXTX_INIT,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	params->map_addr = resp.map_addr;
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_rxtx_port_init(struct prestera_port *port)
> >+{
> >+	struct prestera_msg_rxtx_port_req req = {
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_RXTX_PORT_INIT,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_event_handler_register(struct prestera_switch *sw,
> >+				       enum prestera_event_type type,
> >+				       prestera_event_cb_t fn,
> >+				       void *arg)
> >+{
> >+	struct prestera_fw_event_handler *eh;
> >+
> >+	eh = __find_event_handler(sw, type);
> >+	if (eh)
> >+		return -EEXIST;
> >+	eh = kmalloc(sizeof(*eh), GFP_KERNEL);
> >+	if (!eh)
> >+		return -ENOMEM;
> >+
> >+	eh->type = type;
> >+	eh->func = fn;
> >+	eh->arg = arg;
> >+
> >+	INIT_LIST_HEAD(&eh->list);
> >+
> >+	list_add_rcu(&eh->list, &sw->event_handlers);
> >+
> >+	return 0;
> >+}
> >+
> >+void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
> >+					  enum prestera_event_type type,
> >+					  prestera_event_cb_t fn)
> >+{
> >+	struct prestera_fw_event_handler *eh;
> >+
> >+	eh = __find_event_handler(sw, type);
> >+	if (!eh)
> >+		return;
> >+
> >+	list_del_rcu(&eh->list);
> >+	synchronize_rcu();
> >+	kfree(eh);
> >+}
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
> >new file mode 100644
> >index 000000000000..acb0e31d6684
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
> >@@ -0,0 +1,71 @@
> >+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+ *
> >+ * Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved.
> >+ *
> >+ */
> >+
> >+#ifndef _PRESTERA_HW_H_
> >+#define _PRESTERA_HW_H_
> >+
> >+#include <linux/types.h>
> >+
> >+enum {
> >+	PRESTERA_PORT_TYPE_NONE,
> >+	PRESTERA_PORT_TYPE_TP,
> >+
> >+	PRESTERA_PORT_TYPE_MAX,
> >+};
> >+
> >+enum {
> >+	PRESTERA_PORT_FEC_OFF,
> >+
> >+	PRESTERA_PORT_FEC_MAX,
> >+};
> >+
> >+struct prestera_switch;
> >+struct prestera_port;
> >+struct prestera_port_stats;
> >+struct prestera_port_caps;
> >+enum prestera_event_type;
> >+struct prestera_event;
> >+
> >+typedef void (*prestera_event_cb_t)
> >+	(struct prestera_switch *sw, struct prestera_event *evt, void *arg);
> >+
> >+struct prestera_rxtx_params;
> >+
> >+/* Switch API */
> >+int prestera_hw_switch_init(struct prestera_switch *sw);
> >+int prestera_hw_switch_mac_set(struct prestera_switch *sw, char *mac);
> >+
> >+/* Port API */
> >+int prestera_hw_port_info_get(const struct prestera_port *port,
> >+			      u16 *fp_id, u32 *hw_id, u32 *dev_id);
> >+int prestera_hw_port_state_set(const struct prestera_port *port,
> >+			       bool admin_state);
> >+int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu);
> >+int prestera_hw_port_mtu_get(const struct prestera_port *port, u32 *mtu);
> >+int prestera_hw_port_mac_set(const struct prestera_port *port, char *mac);
> >+int prestera_hw_port_mac_get(const struct prestera_port *port, char *mac);
> >+int prestera_hw_port_cap_get(const struct prestera_port *port,
> >+			     struct prestera_port_caps *caps);
> >+int prestera_hw_port_autoneg_set(const struct prestera_port *port,
> >+				 bool autoneg, u64 link_modes, u8 fec);
> >+int prestera_hw_port_stats_get(const struct prestera_port *port,
> >+			       struct prestera_port_stats *stats);
> >+
> >+/* Event handlers */
> >+int prestera_hw_event_handler_register(struct prestera_switch *sw,
> >+				       enum prestera_event_type type,
> >+				       prestera_event_cb_t fn,
> >+				       void *arg);
> >+void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
> >+					  enum prestera_event_type type,
> >+					  prestera_event_cb_t fn);
> >+
> >+/* RX/TX */
> >+int prestera_hw_rxtx_init(struct prestera_switch *sw,
> >+			  struct prestera_rxtx_params *params);
> >+int prestera_hw_rxtx_port_init(struct prestera_port *port);
> >+
> >+#endif /* _PRESTERA_HW_H_ */
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c
> >new file mode 100644
> >index 000000000000..556941d97d4d
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c
> >@@ -0,0 +1,825 @@
> >+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
> >+
> >+#include <linux/platform_device.h>
> >+#include <linux/of.h>
> >+#include <linux/of_address.h>
> >+#include <linux/of_device.h>
> >+#include <linux/dmapool.h>
> >+#include <linux/netdevice.h>
> >+#include <linux/etherdevice.h>
> >+#include <linux/if_vlan.h>
> >+
> >+#include "prestera.h"
> >+#include "prestera_hw.h"
> >+#include "prestera_dsa.h"
> >+
> >+struct prestera_sdma_desc {
> >+	__le32 word1;
> >+	__le32 word2;
> >+	__le32 buff;
> >+	__le32 next;
> >+} __packed __aligned(16);
> >+
> >+#define SDMA_BUFF_SIZE_MAX	1544
> >+
> >+#define SDMA_RX_DESC_PKT_LEN(desc) \
> >+	((le32_to_cpu((desc)->word2) >> 16) & 0x3FFF)
> >+
> >+#define SDMA_RX_DESC_OWNER(desc) \
> >+	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
> >+
> >+#define SDMA_RX_DESC_CPU_OWN	0
> >+#define SDMA_RX_DESC_DMA_OWN	1
> >+
> >+#define SDMA_RX_QUEUE_NUM	8
> >+
> >+#define SDMA_RX_DESC_PER_Q	1000
> >+
> >+#define SDMA_TX_DESC_PER_Q	1000
> >+#define SDMA_TX_MAX_BURST	64
> >+
> >+#define SDMA_TX_DESC_OWNER(desc) \
> >+	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
> >+
> >+#define SDMA_TX_DESC_CPU_OWN	0
> >+#define SDMA_TX_DESC_DMA_OWN	1
> >+
> >+#define SDMA_TX_DESC_IS_SENT(desc) \
> >+	(SDMA_TX_DESC_OWNER(desc) == SDMA_TX_DESC_CPU_OWN)
> >+
> >+#define SDMA_TX_DESC_LAST	BIT(20)
> >+#define SDMA_TX_DESC_FIRST	BIT(21)
> >+#define SDMA_TX_DESC_SINGLE	(SDMA_TX_DESC_FIRST | SDMA_TX_DESC_LAST)
> >+#define SDMA_TX_DESC_CALC_CRC	BIT(12)
> >+
> >+#define SDMA_RX_INTR_MASK_REG		0x2814
> >+#define SDMA_RX_QUEUE_STATUS_REG	0x2680
> >+#define SDMA_RX_QUEUE_DESC_REG(n)	(0x260C + (n) * 16)
> >+
> >+#define SDMA_TX_QUEUE_DESC_REG		0x26C0
> >+#define SDMA_TX_QUEUE_START_REG		0x2868
> 
> You forgot to prefix these.
> 
> 
> >+
> >+struct prestera_sdma_buf {
> >+	struct prestera_sdma_desc *desc;
> >+	dma_addr_t desc_dma;
> >+	struct sk_buff *skb;
> >+	dma_addr_t buf_dma;
> >+	bool is_used;
> >+};
> >+
> >+struct prestera_rx_ring {
> >+	struct prestera_sdma_buf *bufs;
> >+	int next_rx;
> >+};
> >+
> >+struct prestera_tx_ring {
> >+	struct prestera_sdma_buf *bufs;
> >+	int next_tx;
> >+	int max_burst;
> >+	int burst;
> >+};
> >+
> >+struct prestera_sdma {
> >+	struct prestera_rx_ring rx_ring[SDMA_RX_QUEUE_NUM];
> >+	struct prestera_tx_ring tx_ring;
> >+	const struct prestera_switch *sw;
> >+	struct dma_pool *desc_pool;
> >+	struct work_struct tx_work;
> >+	struct napi_struct rx_napi;
> >+	struct net_device napi_dev;
> >+	u32 map_addr;
> >+	u64 dma_mask;
> >+};
> >+
> >+struct prestera_rxtx {
> >+	struct prestera_sdma sdma;
> >+};
> >+
> >+static int prestera_sdma_buf_init(struct prestera_sdma *sdma,
> >+				  struct prestera_sdma_buf *buf)
> >+{
> >+	struct device *dma_dev = sdma->sw->dev->dev;
> >+	struct prestera_sdma_desc *desc;
> >+	dma_addr_t dma;
> >+
> >+	desc = dma_pool_alloc(sdma->desc_pool, GFP_DMA | GFP_KERNEL, &dma);
> >+	if (!desc)
> >+		return -ENOMEM;
> >+
> >+	if (dma + sizeof(struct prestera_sdma_desc) > sdma->dma_mask) {
> >+		dev_err(dma_dev, "failed to alloc desc\n");
> >+		dma_pool_free(sdma->desc_pool, desc, dma);
> >+		return -ENOMEM;
> >+	}
> >+
> >+	buf->buf_dma = DMA_MAPPING_ERROR;
> >+	buf->desc_dma = dma;
> >+	buf->desc = desc;
> >+	buf->skb = NULL;
> >+
> >+	return 0;
> >+}
> >+
> >+static u32 prestera_sdma_map(struct prestera_sdma *sdma, dma_addr_t pa)
> >+{
> >+	return sdma->map_addr + pa;
> >+}
> >+
> >+static void prestera_sdma_rx_desc_set_len(struct prestera_sdma_desc *desc,
> >+					  size_t val)
> >+{
> >+	u32 word = le32_to_cpu(desc->word2);
> >+
> >+	word = (word & ~GENMASK(15, 0)) | val;
> >+	desc->word2 = cpu_to_le32(word);
> >+}
> >+
> >+static void prestera_sdma_rx_desc_init(struct prestera_sdma *sdma,
> >+				       struct prestera_sdma_desc *desc,
> >+				       dma_addr_t buf)
> >+{
> >+	prestera_sdma_rx_desc_set_len(desc, SDMA_BUFF_SIZE_MAX);
> >+	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
> >+
> >+	/* make sure buffer is set before reset the descriptor */
> >+	wmb();
> >+
> >+	desc->word1 = cpu_to_le32(0xA0000000);
> >+}
> >+
> >+static void prestera_sdma_rx_desc_set_next(struct prestera_sdma *sdma,
> >+					   struct prestera_sdma_desc *desc,
> >+					   dma_addr_t next)
> >+{
> >+	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
> >+}
> >+
> >+static int prestera_sdma_rx_skb_alloc(struct prestera_sdma *sdma,
> >+				      struct prestera_sdma_buf *buf)
> >+{
> >+	struct device *dev = sdma->sw->dev->dev;
> >+	struct sk_buff *skb;
> >+	dma_addr_t dma;
> >+
> >+	skb = alloc_skb(SDMA_BUFF_SIZE_MAX, GFP_DMA | GFP_ATOMIC);
> >+	if (!skb)
> >+		return -ENOMEM;
> >+
> >+	dma = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE);
> >+
> >+	if (dma_mapping_error(dev, dma))
> >+		goto err_dma_map;
> >+	if (dma + skb->len > sdma->dma_mask)
> >+		goto err_dma_range;
> >+
> >+	if (buf->skb)
> >+		dma_unmap_single(dev, buf->buf_dma, buf->skb->len,
> >+				 DMA_FROM_DEVICE);
> >+
> >+	buf->buf_dma = dma;
> >+	buf->skb = skb;
> >+	return 0;
> >+
> >+err_dma_range:
> >+	dma_unmap_single(dev, dma, skb->len, DMA_FROM_DEVICE);
> >+err_dma_map:
> >+	kfree_skb(skb);
> >+
> >+	return -ENOMEM;
> >+}
> >+
> >+static struct sk_buff *prestera_sdma_rx_skb_get(struct prestera_sdma *sdma,
> >+						struct prestera_sdma_buf *buf)
> >+{
> >+	dma_addr_t buf_dma = buf->buf_dma;
> >+	struct sk_buff *skb = buf->skb;
> >+	u32 len = skb->len;
> >+	int err;
> >+
> >+	err = prestera_sdma_rx_skb_alloc(sdma, buf);
> >+	if (err) {
> >+		buf->buf_dma = buf_dma;
> >+		buf->skb = skb;
> >+
> >+		skb = alloc_skb(skb->len, GFP_ATOMIC);
> >+		if (skb) {
> >+			skb_put(skb, len);
> >+			skb_copy_from_linear_data(buf->skb, skb->data, len);
> >+		}
> >+	}
> >+
> >+	prestera_sdma_rx_desc_init(sdma, buf->desc, buf->buf_dma);
> >+
> >+	return skb;
> >+}
> >+
> >+static int prestera_rxtx_process_skb(struct sk_buff *skb)
> >+{
> >+	const struct prestera_port *port;
> >+	struct prestera_dsa dsa;
> >+	u32 hw_port, hw_id;
> >+	int err;
> >+
> >+	skb_pull(skb, ETH_HLEN);
> >+
> >+	/* ethertype field is part of the dsa header */
> >+	err = prestera_dsa_parse(&dsa, skb->data - ETH_TLEN);
> >+	if (err)
> >+		return err;
> >+
> >+	hw_port = dsa.port_num;
> >+	hw_id = dsa.hw_dev_num;
> >+
> >+	port = prestera_port_find_by_hwid(hw_id, hw_port);
> >+	if (unlikely(!port)) {
> >+		pr_warn_ratelimited("prestera: received pkt for non-existent port(%u, %u)\n",
> >+				    hw_id, hw_port);
> >+		return -EEXIST;
> >+	}
> >+
> >+	if (unlikely(!pskb_may_pull(skb, PRESTERA_DSA_HLEN)))
> >+		return -EINVAL;
> >+
> >+	/* remove DSA tag and update checksum */
> >+	skb_pull_rcsum(skb, PRESTERA_DSA_HLEN);
> >+
> >+	memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - PRESTERA_DSA_HLEN,
> >+		ETH_ALEN * 2);
> >+
> >+	skb_push(skb, ETH_HLEN);
> >+
> >+	skb->protocol = eth_type_trans(skb, port->dev);
> >+
> >+	if (dsa.vlan.is_tagged) {
> >+		u16 tci = dsa.vlan.vid & VLAN_VID_MASK;
> >+
> >+		tci |= dsa.vlan.vpt << VLAN_PRIO_SHIFT;
> >+		if (dsa.vlan.cfi_bit)
> >+			tci |= VLAN_CFI_MASK;
> >+
> >+		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci);
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+static int prestera_sdma_rx_poll(struct napi_struct *napi, int budget)
> >+{
> >+	unsigned int qmask = GENMASK(SDMA_RX_QUEUE_NUM - 1, 0);
> >+	struct prestera_sdma *sdma;
> >+	unsigned int rxq_done_map = 0;
> >+	struct list_head rx_list;
> >+	int pkts_done = 0;
> >+	int q;
> >+
> >+	INIT_LIST_HEAD(&rx_list);
> >+
> >+	sdma = container_of(napi, struct prestera_sdma, rx_napi);
> >+
> >+	while (pkts_done < budget && rxq_done_map != qmask) {
> >+		for (q = 0; q < SDMA_RX_QUEUE_NUM && pkts_done < budget; q++) {
> >+			struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> >+			int buf_idx = ring->next_rx;
> >+			struct prestera_sdma_desc *desc;
> >+			struct prestera_sdma_buf *buf;
> >+			struct sk_buff *skb;
> >+
> >+			buf = &ring->bufs[buf_idx];
> >+			desc = buf->desc;
> >+
> >+			if (SDMA_RX_DESC_OWNER(desc) != SDMA_RX_DESC_CPU_OWN) {
> >+				rxq_done_map |= BIT(q);
> >+				continue;
> >+			} else {
> >+				rxq_done_map &= ~BIT(q);
> >+			}
> >+
> >+			pkts_done++;
> >+
> >+			__skb_trim(buf->skb, SDMA_RX_DESC_PKT_LEN(desc));
> >+
> >+			skb = prestera_sdma_rx_skb_get(sdma, buf);
> >+			if (!skb)
> >+				goto rx_next_buf;
> >+
> >+			if (unlikely(prestera_rxtx_process_skb(skb)))
> >+				goto rx_next_buf;
> >+
> >+			list_add_tail(&skb->list, &rx_list);
> >+rx_next_buf:
> >+			ring->next_rx = (buf_idx + 1) % SDMA_RX_DESC_PER_Q;
> >+		}
> >+	}
> >+
> >+	if (pkts_done < budget && napi_complete_done(napi, pkts_done))
> >+		prestera_write(sdma->sw, SDMA_RX_INTR_MASK_REG, 0xff << 2);
> >+
> >+	netif_receive_skb_list(&rx_list);
> >+
> >+	return pkts_done;
> >+}
> >+
> >+static void prestera_sdma_rx_fini(struct prestera_sdma *sdma)
> >+{
> >+	int q, b;
> >+
> >+	/* disable all rx queues */
> >+	prestera_write(sdma->sw, SDMA_RX_QUEUE_STATUS_REG, 0xff00);
> >+
> >+	for (q = 0; q < SDMA_RX_QUEUE_NUM; q++) {
> >+		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> >+
> >+		if (!ring->bufs)
> >+			break;
> >+
> >+		for (b = 0; b < SDMA_RX_DESC_PER_Q; b++) {
> >+			struct prestera_sdma_buf *buf = &ring->bufs[b];
> >+
> >+			if (buf->desc_dma)
> >+				dma_pool_free(sdma->desc_pool, buf->desc,
> >+					      buf->desc_dma);
> >+
> >+			if (!buf->skb)
> >+				continue;
> >+
> >+			if (buf->buf_dma != DMA_MAPPING_ERROR)
> >+				dma_unmap_single(sdma->sw->dev->dev,
> >+						 buf->buf_dma, buf->skb->len,
> >+						 DMA_FROM_DEVICE);
> >+			kfree_skb(buf->skb);
> >+		}
> >+	}
> >+}
> >+
> >+static int prestera_sdma_rx_init(struct prestera_sdma *sdma)
> >+{
> >+	int q, b;
> >+	int err;
> >+
> >+	/* disable all rx queues */
> >+	prestera_write(sdma->sw, SDMA_RX_QUEUE_STATUS_REG, 0xff00);
> >+
> >+	for (q = 0; q < SDMA_RX_QUEUE_NUM; q++) {
> >+		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> >+		struct prestera_sdma_buf *head;
> >+
> >+		ring->bufs = kmalloc_array(SDMA_RX_DESC_PER_Q, sizeof(*head),
> >+					   GFP_KERNEL);
> >+		if (!ring->bufs)
> >+			return -ENOMEM;
> >+
> >+		head = &ring->bufs[0];
> >+		ring->next_rx = 0;
> >+
> >+		for (b = 0; b < SDMA_RX_DESC_PER_Q; b++) {
> >+			struct prestera_sdma_buf *buf = &ring->bufs[b];
> >+
> >+			err = prestera_sdma_buf_init(sdma, buf);
> >+			if (err)
> >+				return err;
> >+
> >+			err = prestera_sdma_rx_skb_alloc(sdma, buf);
> >+			if (err)
> >+				return err;
> >+
> >+			prestera_sdma_rx_desc_init(sdma, buf->desc,
> >+						   buf->buf_dma);
> >+
> >+			if (b == 0)
> >+				continue;
> >+
> >+			prestera_sdma_rx_desc_set_next(sdma,
> >+						       ring->bufs[b - 1].desc,
> >+						       buf->desc_dma);
> >+
> >+			if (b == SDMA_RX_DESC_PER_Q - 1)
> >+				prestera_sdma_rx_desc_set_next(sdma, buf->desc,
> >+							       head->desc_dma);
> >+		}
> >+
> >+		prestera_write(sdma->sw, SDMA_RX_QUEUE_DESC_REG(q),
> >+			       prestera_sdma_map(sdma, head->desc_dma));
> >+	}
> >+
> >+	/* make sure all rx descs are filled before enabling all rx queues */
> >+	wmb();
> >+
> >+	prestera_write(sdma->sw, SDMA_RX_QUEUE_STATUS_REG, 0xff);
> >+
> >+	return 0;
> >+}
> >+
> >+static void prestera_sdma_tx_desc_init(struct prestera_sdma *sdma,
> >+				       struct prestera_sdma_desc *desc)
> >+{
> >+	desc->word1 = cpu_to_le32(SDMA_TX_DESC_SINGLE | SDMA_TX_DESC_CALC_CRC);
> >+	desc->word2 = 0;
> >+}
> >+
> >+static void prestera_sdma_tx_desc_set_next(struct prestera_sdma *sdma,
> >+					   struct prestera_sdma_desc *desc,
> >+					   dma_addr_t next)
> >+{
> >+	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
> >+}
> >+
> >+static void prestera_sdma_tx_desc_set_buf(struct prestera_sdma *sdma,
> >+					  struct prestera_sdma_desc *desc,
> >+					  dma_addr_t buf, size_t len)
> >+{
> >+	u32 word = le32_to_cpu(desc->word2);
> >+
> >+	word = (word & ~GENMASK(30, 16)) | ((len + ETH_FCS_LEN) << 16);
> >+
> >+	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
> >+	desc->word2 = cpu_to_le32(word);
> >+}
> >+
> >+static void prestera_sdma_tx_desc_xmit(struct prestera_sdma_desc *desc)
> >+{
> >+	u32 word = le32_to_cpu(desc->word1);
> >+
> >+	word |= (SDMA_TX_DESC_DMA_OWN << 31);
> 
> Drop the ()s here.
> 
> 
> >+
> >+	/* make sure everything is written before enable xmit */
> >+	wmb();
> >+
> >+	desc->word1 = cpu_to_le32(word);
> >+}
> >+
> >+static int prestera_sdma_tx_buf_map(struct prestera_sdma *sdma,
> >+				    struct prestera_sdma_buf *buf,
> >+				    struct sk_buff *skb)
> >+{
> >+	struct device *dma_dev = sdma->sw->dev->dev;
> >+	struct sk_buff *new_skb;
> >+	size_t len = skb->len;
> >+	dma_addr_t dma;
> >+
> >+	dma = dma_map_single(dma_dev, skb->data, len, DMA_TO_DEVICE);
> >+	if (!dma_mapping_error(dma_dev, dma) && dma + len <= sdma->dma_mask) {
> >+		buf->buf_dma = dma;
> >+		buf->skb = skb;
> >+		return 0;
> >+	}
> >+
> >+	if (!dma_mapping_error(dma_dev, dma))
> >+		dma_unmap_single(dma_dev, dma, len, DMA_TO_DEVICE);
> >+
> >+	new_skb = alloc_skb(len, GFP_ATOMIC | GFP_DMA);
> >+	if (!new_skb)
> >+		goto err_alloc_skb;
> >+
> >+	dma = dma_map_single(dma_dev, new_skb->data, len, DMA_TO_DEVICE);
> >+	if (dma_mapping_error(dma_dev, dma))
> >+		goto err_dma_map;
> >+	if (dma + len > sdma->dma_mask)
> >+		goto err_dma_range;
> >+
> >+	skb_copy_from_linear_data(skb, skb_put(new_skb, len), len);
> >+
> >+	dev_consume_skb_any(skb);
> >+
> >+	buf->skb = new_skb;
> >+	buf->buf_dma = dma;
> >+
> >+	return 0;
> >+
> >+err_dma_range:
> >+	dma_unmap_single(dma_dev, dma, len, DMA_TO_DEVICE);
> >+err_dma_map:
> >+	dev_kfree_skb(new_skb);
> >+err_alloc_skb:
> >+	dev_kfree_skb(skb);
> >+
> >+	return -ENOMEM;
> >+}
> >+
> >+static void prestera_sdma_tx_buf_unmap(struct prestera_sdma *sdma,
> >+				       struct prestera_sdma_buf *buf)
> >+{
> >+	struct device *dma_dev = sdma->sw->dev->dev;
> >+
> >+	dma_unmap_single(dma_dev, buf->buf_dma, buf->skb->len, DMA_TO_DEVICE);
> >+}
> >+
> >+static void prestera_sdma_tx_recycle_work_fn(struct work_struct *work)
> >+{
> >+	struct prestera_tx_ring *tx_ring;
> >+	struct prestera_sdma *sdma;
> >+	struct device *dma_dev;
> >+	int b;
> >+
> >+	sdma = container_of(work, struct prestera_sdma, tx_work);
> >+
> >+	dma_dev = sdma->sw->dev->dev;
> >+	tx_ring = &sdma->tx_ring;
> >+
> >+	for (b = 0; b < SDMA_TX_DESC_PER_Q; b++) {
> >+		struct prestera_sdma_buf *buf = &tx_ring->bufs[b];
> >+
> >+		if (!buf->is_used)
> >+			continue;
> >+
> >+		if (!SDMA_TX_DESC_IS_SENT(buf->desc))
> >+			continue;
> >+
> >+		prestera_sdma_tx_buf_unmap(sdma, buf);
> >+		dev_consume_skb_any(buf->skb);
> >+		buf->skb = NULL;
> >+
> >+		/* make sure everything is cleaned up */
> >+		wmb();
> >+
> >+		buf->is_used = false;
> >+	}
> >+}
> >+
> >+static int prestera_sdma_tx_init(struct prestera_sdma *sdma)
> >+{
> >+	struct prestera_tx_ring *tx_ring = &sdma->tx_ring;
> >+	struct prestera_sdma_buf *head;
> >+	int err;
> >+	int b;
> >+
> >+	INIT_WORK(&sdma->tx_work, prestera_sdma_tx_recycle_work_fn);
> >+
> >+	tx_ring->bufs = kmalloc_array(SDMA_TX_DESC_PER_Q, sizeof(*head),
> >+				      GFP_KERNEL);
> >+	if (!tx_ring->bufs)
> >+		return -ENOMEM;
> >+
> >+	head = &tx_ring->bufs[0];
> >+
> >+	tx_ring->max_burst = SDMA_TX_MAX_BURST;
> >+	tx_ring->burst = tx_ring->max_burst;
> >+	tx_ring->next_tx = 0;
> >+
> >+	for (b = 0; b < SDMA_TX_DESC_PER_Q; b++) {
> >+		struct prestera_sdma_buf *buf = &tx_ring->bufs[b];
> >+
> >+		err = prestera_sdma_buf_init(sdma, buf);
> >+		if (err)
> >+			return err;
> >+
> >+		prestera_sdma_tx_desc_init(sdma, buf->desc);
> >+
> >+		buf->is_used = false;
> >+
> >+		if (b == 0)
> >+			continue;
> >+
> >+		prestera_sdma_tx_desc_set_next(sdma, tx_ring->bufs[b - 1].desc,
> >+					       buf->desc_dma);
> >+
> >+		if (b == SDMA_TX_DESC_PER_Q - 1)
> >+			prestera_sdma_tx_desc_set_next(sdma, buf->desc,
> >+						       head->desc_dma);
> >+	}
> >+
> >+	/* make sure descriptors are written */
> >+	wmb();
> >+
> >+	prestera_write(sdma->sw, SDMA_TX_QUEUE_DESC_REG,
> >+		       prestera_sdma_map(sdma, head->desc_dma));
> >+
> >+	return 0;
> >+}
> >+
> >+static void prestera_sdma_tx_fini(struct prestera_sdma *sdma)
> >+{
> >+	struct prestera_tx_ring *ring = &sdma->tx_ring;
> >+	int b;
> >+
> >+	cancel_work_sync(&sdma->tx_work);
> >+
> >+	if (!ring->bufs)
> >+		return;
> >+
> >+	for (b = 0; b < SDMA_TX_DESC_PER_Q; b++) {
> >+		struct prestera_sdma_buf *buf = &ring->bufs[b];
> >+
> >+		if (buf->desc)
> >+			dma_pool_free(sdma->desc_pool, buf->desc,
> >+				      buf->desc_dma);
> >+
> >+		if (!buf->skb)
> >+			continue;
> >+
> >+		dma_unmap_single(sdma->sw->dev->dev, buf->buf_dma,
> >+				 buf->skb->len, DMA_TO_DEVICE);
> >+
> >+		dev_consume_skb_any(buf->skb);
> >+	}
> >+}
> >+
> >+static void prestera_rxtx_handle_event(struct prestera_switch *sw,
> >+				       struct prestera_event *evt,
> >+				       void *arg)
> >+{
> >+	struct prestera_sdma *sdma = arg;
> >+
> >+	if (evt->id != PRESTERA_RXTX_EVENT_RCV_PKT)
> >+		return;
> >+
> >+	prestera_write(sdma->sw, SDMA_RX_INTR_MASK_REG, 0);
> >+	napi_schedule(&sdma->rx_napi);
> >+}
> >+
> >+int prestera_sdma_switch_init(struct prestera_switch *sw)
> >+{
> >+	struct prestera_sdma *sdma = &sw->rxtx->sdma;
> >+	struct device *dev = sw->dev->dev;
> >+	struct prestera_rxtx_params p;
> >+	int err;
> >+
> >+	p.use_sdma = true;
> >+
> >+	err = prestera_hw_rxtx_init(sw, &p);
> >+	if (err) {
> >+		dev_err(dev, "failed to init rxtx by hw\n");
> >+		return err;
> >+	}
> >+
> >+	sdma->dma_mask = dma_get_mask(dev);
> >+	sdma->map_addr = p.map_addr;
> >+	sdma->sw = sw;
> >+
> >+	sdma->desc_pool = dma_pool_create("desc_pool", dev,
> >+					  sizeof(struct prestera_sdma_desc),
> >+					  16, 0);
> >+	if (!sdma->desc_pool)
> >+		return -ENOMEM;
> >+
> >+	err = prestera_sdma_rx_init(sdma);
> >+	if (err) {
> >+		dev_err(dev, "failed to init rx ring\n");
> >+		goto err_rx_init;
> >+	}
> >+
> >+	err = prestera_sdma_tx_init(sdma);
> >+	if (err) {
> >+		dev_err(dev, "failed to init tx ring\n");
> >+		goto err_tx_init;
> >+	}
> >+
> >+	err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_RXTX,
> >+						 prestera_rxtx_handle_event,
> >+						 sdma);
> >+	if (err)
> >+		goto err_evt_register;
> >+
> >+	init_dummy_netdev(&sdma->napi_dev);
> >+
> >+	netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll, 64);
> >+	napi_enable(&sdma->rx_napi);
> >+
> >+	return 0;
> >+
> >+err_evt_register:
> >+err_tx_init:
> >+	prestera_sdma_tx_fini(sdma);
> >+err_rx_init:
> >+	prestera_sdma_rx_fini(sdma);
> >+
> >+	dma_pool_destroy(sdma->desc_pool);
> >+	return err;
> >+}
> >+
> >+void prestera_sdma_switch_fini(struct prestera_switch *sw)
> >+{
> >+	struct prestera_sdma *sdma = &sw->rxtx->sdma;
> >+
> >+	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
> >+					     prestera_rxtx_handle_event);
> >+	napi_disable(&sdma->rx_napi);
> >+	netif_napi_del(&sdma->rx_napi);
> >+	prestera_sdma_rx_fini(sdma);
> >+	prestera_sdma_tx_fini(sdma);
> >+	dma_pool_destroy(sdma->desc_pool);
> >+}
> >+
> >+static int prestera_sdma_tx_wait(struct prestera_sdma *sdma,
> >+				 struct prestera_tx_ring *tx_ring)
> >+{
> >+	int tx_retry_num = 10 * tx_ring->max_burst;
> >+
> >+	while (--tx_retry_num) {
> >+		if (!(prestera_read(sdma->sw, SDMA_TX_QUEUE_START_REG) & 1))
> >+			return 0;
> >+
> >+		udelay(1);
> >+	}
> >+
> >+	return -EBUSY;
> >+}
> >+
> >+static void prestera_sdma_tx_start(struct prestera_sdma *sdma)
> >+{
> >+	prestera_write(sdma->sw, SDMA_TX_QUEUE_START_REG, 1);
> >+	schedule_work(&sdma->tx_work);
> >+}
> >+
> >+netdev_tx_t prestera_sdma_xmit(struct prestera_sdma *sdma, struct sk_buff *skb)
> >+{
> >+	struct device *dma_dev = sdma->sw->dev->dev;
> >+	struct prestera_tx_ring *tx_ring;
> >+	struct net_device *dev = skb->dev;
> >+	struct prestera_sdma_buf *buf;
> >+	int err;
> >+
> >+	tx_ring = &sdma->tx_ring;
> >+
> >+	buf = &tx_ring->bufs[tx_ring->next_tx];
> >+	if (buf->is_used) {
> >+		schedule_work(&sdma->tx_work);
> >+		goto drop_skb;
> >+	}
> 
> What is preventing 2 CPUs to get here and work with the same buf?

I assume you mean serialization between the recycling work and xmit
context ? Actually they are just updating 'is_used' field which
allows to use or free, what I can see is that may be I need to use
something like READ_ONCE/WRITE_ONCE, but the rest looks safe for me:

1) recycler updates is_used=false only after fully freeing the buffer,
and only if it was set to true.

2) xmit context gets next buffer to use only if it is freed
(is_used=false), and sets it to true after buffer is ready to be sent.

So, yes these contexts both update this field but in strict sequence.

If you mean of protecting of xmit on several CPUS so, the xmit should be
serialized on kernel, and the driver uses one queue which (as I
underand) is bound to particular CPU.

> 
> 
> 
> >+
> >+	if (unlikely(eth_skb_pad(skb)))
> >+		goto drop_skb_nofree;
> >+
> >+	err = prestera_sdma_tx_buf_map(sdma, buf, skb);
> >+	if (err)
> >+		goto drop_skb;
> >+
> >+	prestera_sdma_tx_desc_set_buf(sdma, buf->desc, buf->buf_dma, skb->len);
> >+
> >+	dma_sync_single_for_device(dma_dev, buf->buf_dma, skb->len,
> >+				   DMA_TO_DEVICE);
> >+
> >+	if (!tx_ring->burst--) {
> >+		tx_ring->burst = tx_ring->max_burst;
> >+
> >+		err = prestera_sdma_tx_wait(sdma, tx_ring);
> >+		if (err)
> >+			goto drop_skb_unmap;
> >+	}
> >+
> >+	tx_ring->next_tx = (tx_ring->next_tx + 1) % SDMA_TX_DESC_PER_Q;
> >+	prestera_sdma_tx_desc_xmit(buf->desc);
> >+	buf->is_used = true;
> >+
> >+	prestera_sdma_tx_start(sdma);
> >+
> >+	return NETDEV_TX_OK;
> >+
> >+drop_skb_unmap:
> >+	prestera_sdma_tx_buf_unmap(sdma, buf);
> >+drop_skb:
> >+	dev_consume_skb_any(skb);
> >+drop_skb_nofree:
> >+	dev->stats.tx_dropped++;
> >+	return NETDEV_TX_OK;
> >+}
> >+
> >+int prestera_rxtx_switch_init(struct prestera_switch *sw)
> >+{
> >+	struct prestera_rxtx *rxtx;
> >+
> >+	rxtx = kzalloc(sizeof(*rxtx), GFP_KERNEL);
> >+	if (!rxtx)
> >+		return -ENOMEM;
> >+
> >+	sw->rxtx = rxtx;
> >+
> >+	return prestera_sdma_switch_init(sw);
> >+}
> >+
> >+void prestera_rxtx_switch_fini(struct prestera_switch *sw)
> >+{
> >+	prestera_sdma_switch_fini(sw);
> >+	kfree(sw->rxtx);
> >+}
> >+
> >+int prestera_rxtx_port_init(struct prestera_port *port)
> >+{
> >+	int err;
> >+
> >+	err = prestera_hw_rxtx_port_init(port);
> >+	if (err)
> >+		return err;
> >+
> >+	port->dev->needed_headroom = PRESTERA_DSA_HLEN + ETH_FCS_LEN;
> >+	return 0;
> >+}
> >+
> >+netdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff *skb)
> 
> Why this has "rx" in the name??
> 
> 
> >+{
> >+	struct prestera_dsa dsa;
> >+
> >+	dsa.hw_dev_num = port->dev_id;
> >+	dsa.port_num = port->hw_id;
> >+
> >+	if (skb_cow_head(skb, PRESTERA_DSA_HLEN) < 0)
> >+		return NET_XMIT_DROP;
> >+
> >+	skb_push(skb, PRESTERA_DSA_HLEN);
> >+	memmove(skb->data, skb->data + PRESTERA_DSA_HLEN, 2 * ETH_ALEN);
> >+
> >+	if (prestera_dsa_build(&dsa, skb->data + 2 * ETH_ALEN) != 0)
> >+		return NET_XMIT_DROP;
> >+
> >+	return prestera_sdma_xmit(&port->sw->rxtx->sdma, skb);
> >+}
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_rxtx.h b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.h
> >new file mode 100644
> >index 000000000000..bbbadfa5accf
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.h
> >@@ -0,0 +1,21 @@
> >+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+ *
> >+ * Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved.
> >+ *
> >+ */
> >+
> >+#ifndef _PRESTERA_RXTX_H_
> >+#define _PRESTERA_RXTX_H_
> >+
> >+#include <linux/netdevice.h>
> >+
> >+#include "prestera.h"
> >+
> >+int prestera_rxtx_switch_init(struct prestera_switch *sw);
> >+void prestera_rxtx_switch_fini(struct prestera_switch *sw);
> >+
> >+int prestera_rxtx_port_init(struct prestera_port *port);
> >+
> >+netdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff *skb);
> >+
> >+#endif /* _PRESTERA_RXTX_H_ */
> >-- 
> >2.17.1
> >