[...] >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? >+ >+ 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 >