Mon, May 11, 2020 at 09:24:22PM CEST, vadym.koc...@plvision.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 >> >