[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200512055536.GM2245@nanopsycho>
Date: Tue, 12 May 2020 07:55:36 +0200
From: Jiri Pirko <jiri@...nulli.us>
To: Vadym Kochan <vadym.kochan@...ision.eu>
Cc: netdev@...r.kernel.org, "David S. Miller" <davem@...emloft.net>,
Oleksandr Mazur <oleksandr.mazur@...ision.eu>,
Serhiy Boiko <serhiy.boiko@...ision.eu>,
Serhiy Pshyk <serhiy.pshyk@...ision.eu>,
Volodymyr Mytnyk <volodymyr.mytnyk@...ision.eu>,
Taras Chornyi <taras.chornyi@...ision.eu>,
Andrii Savka <andrii.savka@...ision.eu>,
Jiri Pirko <jiri@...lanox.com>,
Ido Schimmel <idosch@...lanox.com>,
Andrew Lunn <andrew@...n.ch>,
Chris Packham <Chris.Packham@...iedtelesis.co.nz>
Subject: Re: [RFC next-next v2 1/5] net: marvell: prestera: Add driver for
Prestera family ASIC devices
Mon, May 11, 2020 at 09:24:22PM CEST, vadym.kochan@...ision.eu wrote:
>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
No.
>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.
How is it serialized? You get here (to prestera_sdma_xmit()) on 2 CPUs
with the same sdma pointer and 2 skbs.
>
>>
>>
>>
>> >+
>> >+ 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
>> >
Powered by blists - more mailing lists