On Thu, May 28, 2020 at 06:12:40PM +0300, Vadym Kochan wrote:
> Marvell Prestera 98DX326x integrates up to 24 ports of 1GbE with 8
> ports of 10GbE uplinks or 2 ports of 40Gbps stacking for a largely
> wireless SMB deployment.
> 
> The current implementation supports only boards designed for the Marvell
> Switchdev solution and requires special firmware.
> 
> The core Prestera switching logic is implemented in prestera_main.c,
> there is an intermediate hw layer between core logic and firmware. It is
> implemented in prestera_hw.c, the purpose of it is to encapsulate hw
> related logic, in future there is a plan to support more devices with
> different HW related configurations.
> 
> This patch contains only basic switch initialization and RX/TX support
> over SDMA mechanism.
> 
> Currently supported devices have DMA access range <= 32bit and require
> ZONE_DMA to be enabled, for such cases SDMA driver checks if the skb
> allocated in proper range supported by the Prestera device.
> 
> Also meanwhile there is no TX interrupt support in current firmware
> version so recycling work is scheduled on each xmit.
> 
> Port's mac address is generated from the switch base mac which may be
> provided via device-tree (static one or as nvme cell), or randomly
> generated.
> 
> Signed-off-by: Andrii Savka <andrii.sa...@plvision.eu>
> Signed-off-by: Oleksandr Mazur <oleksandr.ma...@plvision.eu>
> Signed-off-by: Serhiy Boiko <serhiy.bo...@plvision.eu>
> Signed-off-by: Serhiy Pshyk <serhiy.ps...@plvision.eu>
> Signed-off-by: Taras Chornyi <taras.chor...@plvision.eu>
> Signed-off-by: Volodymyr Mytnyk <volodymyr.myt...@plvision.eu>
> Signed-off-by: Vadym Kochan <vadym.koc...@plvision.eu>
> ---
>  drivers/net/ethernet/marvell/Kconfig          |   1 +
>  drivers/net/ethernet/marvell/Makefile         |   1 +
>  drivers/net/ethernet/marvell/prestera/Kconfig |  13 +
>  .../net/ethernet/marvell/prestera/Makefile    |   4 +
>  .../net/ethernet/marvell/prestera/prestera.h  | 172 ++++
>  .../ethernet/marvell/prestera/prestera_dsa.c  | 134 +++
>  .../ethernet/marvell/prestera/prestera_dsa.h  |  37 +
>  .../ethernet/marvell/prestera/prestera_hw.c   | 610 +++++++++++++
>  .../ethernet/marvell/prestera/prestera_hw.h   |  71 ++
>  .../ethernet/marvell/prestera/prestera_main.c | 506 +++++++++++
>  .../ethernet/marvell/prestera/prestera_rxtx.c | 860 ++++++++++++++++++
>  .../ethernet/marvell/prestera/prestera_rxtx.h |  21 +
>  12 files changed, 2430 insertions(+)
>  create mode 100644 drivers/net/ethernet/marvell/prestera/Kconfig
>  create mode 100644 drivers/net/ethernet/marvell/prestera/Makefile
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera.h
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_dsa.c
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_dsa.h
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_hw.c
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_hw.h
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_main.c
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_rxtx.c
>  create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_rxtx.h
> 
> diff --git a/drivers/net/ethernet/marvell/Kconfig 
> b/drivers/net/ethernet/marvell/Kconfig
> index 3d5caea096fb..74313d9e1fc0 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -171,5 +171,6 @@ config SKY2_DEBUG
>  
>  
>  source "drivers/net/ethernet/marvell/octeontx2/Kconfig"
> +source "drivers/net/ethernet/marvell/prestera/Kconfig"
>  
>  endif # NET_VENDOR_MARVELL
> diff --git a/drivers/net/ethernet/marvell/Makefile 
> b/drivers/net/ethernet/marvell/Makefile
> index 89dea7284d5b..9f88fe822555 100644
> --- a/drivers/net/ethernet/marvell/Makefile
> +++ b/drivers/net/ethernet/marvell/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
>  obj-$(CONFIG_SKGE) += skge.o
>  obj-$(CONFIG_SKY2) += sky2.o
>  obj-y                += octeontx2/
> +obj-y                += prestera/
> diff --git a/drivers/net/ethernet/marvell/prestera/Kconfig 
> b/drivers/net/ethernet/marvell/prestera/Kconfig
> new file mode 100644
> index 000000000000..76b68613ea7a
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Marvell Prestera drivers configuration
> +#
> +
> +config PRESTERA
> +     tristate "Marvell Prestera Switch ASICs support"
> +     depends on NET_SWITCHDEV && VLAN_8021Q
> +     help
> +       This driver supports Marvell Prestera Switch ASICs family.
> +
> +       To compile this driver as a module, choose M here: the
> +       module will be called prestera.
> diff --git a/drivers/net/ethernet/marvell/prestera/Makefile 
> b/drivers/net/ethernet/marvell/prestera/Makefile
> new file mode 100644
> index 000000000000..610d75032b78
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_PRESTERA)       += prestera.o
> +prestera-objs                := prestera_main.o prestera_hw.o prestera_dsa.o 
> \
> +                        prestera_rxtx.o
> diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h 
> b/drivers/net/ethernet/marvell/prestera/prestera.h
> new file mode 100644
> index 000000000000..5079d872e18a
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/prestera.h
> @@ -0,0 +1,172 @@
> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> + *
> + * Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved.
> + *
> + */
> +
> +#ifndef _PRESTERA_H_
> +#define _PRESTERA_H_
> +
> +#include <linux/skbuff.h>
> +#include <linux/notifier.h>
> +#include <uapi/linux/if_ether.h>
> +#include <linux/workqueue.h>
> +
> +struct prestera_fw_rev {
> +     u16 maj;
> +     u16 min;
> +     u16 sub;
> +};
> +
> +struct prestera_port_stats {
> +     u64 good_octets_received;
> +     u64 bad_octets_received;
> +     u64 mac_trans_error;
> +     u64 broadcast_frames_received;
> +     u64 multicast_frames_received;
> +     u64 frames_64_octets;
> +     u64 frames_65_to_127_octets;
> +     u64 frames_128_to_255_octets;
> +     u64 frames_256_to_511_octets;
> +     u64 frames_512_to_1023_octets;
> +     u64 frames_1024_to_max_octets;
> +     u64 excessive_collision;
> +     u64 multicast_frames_sent;
> +     u64 broadcast_frames_sent;
> +     u64 fc_sent;
> +     u64 fc_received;
> +     u64 buffer_overrun;
> +     u64 undersize;
> +     u64 fragments;
> +     u64 oversize;
> +     u64 jabber;
> +     u64 rx_error_frame_received;
> +     u64 bad_crc;
> +     u64 collisions;
> +     u64 late_collision;
> +     u64 unicast_frames_received;
> +     u64 unicast_frames_sent;
> +     u64 sent_multiple;
> +     u64 sent_deferred;
> +     u64 frames_1024_to_1518_octets;
> +     u64 frames_1519_to_max_octets;
> +     u64 good_octets_sent;
> +};
> +
> +struct prestera_port_caps {
> +     u64 supp_link_modes;
> +     u8 supp_fec;
> +     u8 type;
> +     u8 transceiver;
> +};
> +
> +struct prestera_port {
> +     struct net_device *dev;
> +     struct prestera_switch *sw;
> +     u32 id;
> +     u32 hw_id;
> +     u32 dev_id;
> +     u16 fp_id;
> +     bool autoneg;
> +     u64 adver_link_modes;
> +     u8 adver_fec;
> +     struct prestera_port_caps caps;
> +     struct list_head list;
> +     struct {
> +             struct prestera_port_stats stats;
> +             struct delayed_work caching_dw;
> +     } cached_hw_stats;
> +};
> +
> +struct prestera_device {
> +     struct device *dev;
> +     u8 __iomem *ctl_regs;
> +     u8 __iomem *pp_regs;
> +     struct prestera_fw_rev fw_rev;
> +     void *priv;
> +
> +     /* called by device driver to handle received packets */
> +     void (*recv_pkt)(struct prestera_device *dev);
> +
> +     /* called by device driver to pass event up to the higher layer */
> +     int (*recv_msg)(struct prestera_device *dev, u8 *msg, size_t size);
> +
> +     /* called by higher layer to send request to the firmware */
> +     int (*send_req)(struct prestera_device *dev, u8 *in_msg,
> +                     size_t in_size, u8 *out_msg, size_t out_size,
> +                     unsigned int wait);
> +};
> +
> +enum prestera_event_type {
> +     PRESTERA_EVENT_TYPE_UNSPEC,
> +
> +     PRESTERA_EVENT_TYPE_PORT,
> +     PRESTERA_EVENT_TYPE_RXTX,
> +
> +     PRESTERA_EVENT_TYPE_MAX,
> +};
> +
> +enum prestera_rxtx_event_id {
> +     PRESTERA_RXTX_EVENT_UNSPEC,
> +     PRESTERA_RXTX_EVENT_RCV_PKT,
> +};
> +
> +enum prestera_port_event_id {
> +     PRESTERA_PORT_EVENT_UNSPEC,
> +     PRESTERA_PORT_EVENT_STATE_CHANGED,
> +};
> +
> +struct prestera_port_event {
> +     u32 port_id;
> +     union {
> +             u32 oper_state;
> +     } data;
> +};
> +
> +struct prestera_event {
> +     u16 id;
> +     union {
> +             struct prestera_port_event port_evt;
> +     };
> +};
> +
> +struct prestera_rxtx;
> +
> +struct prestera_switch {
> +     struct prestera_device *dev;
> +     struct prestera_rxtx *rxtx;
> +     struct list_head event_handlers;
> +     char base_mac[ETH_ALEN];
> +     struct list_head port_list;
> +     u32 port_count;
> +     u32 mtu_min;
> +     u32 mtu_max;
> +     u8 id;
> +};
> +
> +struct prestera_rxtx_params {
> +     bool use_sdma;
> +     u32 map_addr;
> +};
> +
> +#define prestera_dev(sw)             ((sw)->dev->dev)
> +
> +static inline void prestera_write(const struct prestera_switch *sw,
> +                               unsigned int reg, u32 val)
> +{
> +     writel(val, sw->dev->pp_regs + reg);
> +}
> +
> +static inline u32 prestera_read(const struct prestera_switch *sw,
> +                             unsigned int reg)
> +{
> +     return readl(sw->dev->pp_regs + reg);
> +}
> +
> +int prestera_device_register(struct prestera_device *dev);
> +void prestera_device_unregister(struct prestera_device *dev);
> +
> +struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
> +                                              u32 dev_id, u32 hw_id);
> +
> +#endif /* _PRESTERA_H_ */
> 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..1d95604507a1
> --- /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 PRESTERA_W0_IS_TAGGED        BIT(29)
> +
> +/* TrgDev[4:0] = {Word0[28:24]} */
> +#define PRESTERA_W0_HW_DEV_NUM       GENMASK(28, 24)
> +
> +/* SrcPort/TrgPort extended to 8b
> + * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> + */
> +#define PRESTERA_W0_IFACE_PORT_NUM   GENMASK(23, 19)
> +
> +/* bits 30:31 - TagCommand 1 = FROM_CPU */
> +#define PRESTERA_W0_DSA_CMD          GENMASK(31, 30)
> +
> +/* bits 13:15 -- UP */
> +#define PRESTERA_W0_VPT              GENMASK(15, 13)
> +
> +#define PRESTERA_W0_EXT_BIT          BIT(12)
> +
> +/* bits 0:11 -- VID */
> +#define PRESTERA_W0_VID              GENMASK(11, 0)
> +
> +/* SrcPort/TrgPort extended to 8b
> + * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> + */
> +#define PRESTERA_W1_IFACE_PORT_NUM   GENMASK(11, 10)
> +
> +#define PRESTERA_W1_EXT_BIT          BIT(31)
> +#define PRESTERA_W1_CFI_BIT          BIT(30)
> +
> +/* SrcPort/TrgPort extended to 8b
> + * SrcPort/TrgPort[7:0] = {Word2[20], Word1[11:10], Word0[23:19]}
> + */
> +#define PRESTERA_W2_IFACE_PORT_NUM   BIT(20)
> +
> +#define PRESTERA_W2_EXT_BIT          BIT(31)
> +
> +/* trgHwDev and trgPort
> + * TrgDev[11:5] = {Word3[6:0]}
> + */
> +#define PRESTERA_W3_HW_DEV_NUM       GENMASK(6, 0)
> +
> +/* VID 16b [15:0] = {Word3[30:27], Word0[11:0]} */
> +#define PRESTERA_W3_VID              GENMASK(30, 27)
> +
> +/* TRGePort[16:0] = {Word3[23:7]} */
> +#define PRESTERA_W3_DST_EPORT        GENMASK(23, 7)
> +
> +#define PRESTERA_DEV_NUM_MASK                GENMASK(11, 5)
> +#define PRESTERA_VID_MASK            GENMASK(15, 12)
> +
> +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(PRESTERA_W0_DSA_CMD, words[0]);
> +
> +     /* only to CPU is supported */
> +     if (unlikely(cmd != PRESTERA_DSA_CMD_TO_CPU))
> +             return -EINVAL;
> +
> +     if (FIELD_GET(PRESTERA_W0_EXT_BIT, words[0]) == 0)
> +             return -EINVAL;
> +     if (FIELD_GET(PRESTERA_W1_EXT_BIT, words[1]) == 0)
> +             return -EINVAL;
> +     if (FIELD_GET(PRESTERA_W2_EXT_BIT, words[2]) == 0)
> +             return -EINVAL;
> +
> +     field = FIELD_GET(PRESTERA_W3_VID, words[3]);
> +
> +     dsa->vlan.is_tagged = (bool)FIELD_GET(PRESTERA_W0_IS_TAGGED, words[0]);
> +     dsa->vlan.cfi_bit = (u8)FIELD_GET(PRESTERA_W1_CFI_BIT, words[1]);
> +     dsa->vlan.vpt = (u8)FIELD_GET(PRESTERA_W0_VPT, words[0]);
> +     dsa->vlan.vid = (u16)FIELD_GET(PRESTERA_W0_VID, words[0]);
> +     dsa->vlan.vid &= ~PRESTERA_VID_MASK;
> +     dsa->vlan.vid |= FIELD_PREP(PRESTERA_VID_MASK, field);
> +
> +     field = FIELD_GET(PRESTERA_W3_HW_DEV_NUM, words[3]);
> +
> +     dsa->hw_dev_num = FIELD_GET(PRESTERA_W0_HW_DEV_NUM, words[0]);
> +     dsa->hw_dev_num &= PRESTERA_W3_HW_DEV_NUM;
> +     dsa->hw_dev_num |= FIELD_PREP(PRESTERA_DEV_NUM_MASK, field);
> +
> +     dsa->port_num = (FIELD_GET(PRESTERA_W0_IFACE_PORT_NUM, words[0]) << 0) |
> +                     (FIELD_GET(PRESTERA_W1_IFACE_PORT_NUM, words[1]) << 5) |
> +                     (FIELD_GET(PRESTERA_W2_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(PRESTERA_W0_DSA_CMD, PRESTERA_DSA_CMD_FROM_CPU);
> +
> +     words[0] |= FIELD_PREP(PRESTERA_W0_HW_DEV_NUM, dsa->hw_dev_num);
> +     words[3] |= FIELD_PREP(PRESTERA_W3_HW_DEV_NUM, (dsa->hw_dev_num >> 5));
> +     words[3] |= FIELD_PREP(PRESTERA_W3_DST_EPORT, dsa->port_num);
> +
> +     words[0] |= FIELD_PREP(PRESTERA_W0_EXT_BIT, 1);
> +     words[1] |= FIELD_PREP(PRESTERA_W1_EXT_BIT, 1);
> +     words[2] |= FIELD_PREP(PRESTERA_W2_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..d653e426dd71
> --- /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 = 0,
> +
> +     /* DSA command is "FROM CPU" */

Nit: "From" ?

> +     PRESTERA_DSA_CMD_FROM_CPU,
> +};
> +
> +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..3aa3974f957a
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c
> @@ -0,0 +1,610 @@
> +// 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 */

Out of curiosity, how long does it actually take you to initialize the
hardware?

Also, I find it useful to note the units in the name, so:

#define PRESTERA_SWITCH_INIT_TIMEOUT_US (30 * 1000 * 1000)

BTW, it says 30 seconds in comment, but the call chain where it is used
is:

prestera_cmd_ret_wait(, PRESTERA_SWITCH_INIT_TIMEOUT)
  __prestera_cmd_ret(..., wait)
    prestera_fw_send_req(..., waitms)
      prestera_fw_cmd_send(..., waitms)
        prestera_fw_wait_reg32(..., waitms)
          readl_poll_timeout(..., waitms * 1000)

So I think you should actually define it as:

#define PRESTERA_SWITCH_INIT_TIMEOUT_MS (30 * 1000)

And rename all these 'wait' arguments to 'waitms' so it's clearer which
unit they expect.

> +#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_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)

You don't have more states here other than OK / FAIL ? It might help you
in debugging if you include them. You might find trace_devlink_hwerr()
useful.

> +             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) {

It does not look that this is always called under RCU which will result
in various splats. For example in the following call path:

prestera_device_register()
  prestera_switch_init()
    prestera_event_handlers_register()
      prestera_hw_event_handler_register()
        __find_event_handler()

You want to make sure that you are testing with various debug options.
For example:

# Debug options
## General debug options
config_enable CONFIG_PREEMPT
config_enable CONFIG_DEBUG_PREEMPT
config_enable CONFIG_DEBUG_INFO
config_enable CONFIG_UNWINDER_ORC
config_enable CONFIG_DYNAMIC_DEBUG
config_enable CONFIG_DEBUG_NOTIFIERS
## Lock debugging
config_enable CONFIG_LOCKDEP
config_enable CONFIG_PROVE_LOCKING
config_enable CONFIG_DEBUG_ATOMIC_SLEEP
config_enable CONFIG_PROVE_RCU
config_enable CONFIG_DEBUG_MUTEXES
config_enable CONFIG_DEBUG_SPINLOCK
config_enable CONFIG_LOCK_STAT
## Memory debugging
config_enable CONFIG_DEBUG_VM
config_enable CONFIG_FORTIFY_SOURCE
config_enable CONFIG_KASAN
config_enable CONFIG_KASAN_EXTRA
config_enable CONFIG_KASAN_INLINE
## Reference counting debugging
config_enable CONFIG_REFCOUNT_FULL
## Lockups debugging
config_enable CONFIG_LOCKUP_DETECTOR
config_enable CONFIG_SOFTLOCKUP_DETECTOR
config_enable CONFIG_HARDLOCKUP_DETECTOR
config_enable CONFIG_DETECT_HUNG_TASK
config_enable CONFIG_WQ_WATCHDOG
config_enable CONFIG_DETECT_HUNG_TASK
config_set_val CONFIG_DEFAULT_HUNG_TASK_TIMEOUT 120
## Undefined behavior debugging
config_enable CONFIG_UBSAN
config_enable CONFIG_UBSAN_SANITIZE_ALL
config_disable CONFIG_UBSAN_ALIGNMENT
config_disable CONFIG_UBSAN_NULL
## Memory debugging
config_enable CONFIG_SLUB_DEBUG
config_enable CONFIG_SLUB_DEBUG_ON
config_enable CONFIG_DEBUG_PAGEALLOC
config_enable CONFIG_DEBUG_KMEMLEAK
config_disable CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF
config_set_val CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE 8192
config_enable CONFIG_DEBUG_STACKOVERFLOW
config_enable CONFIG_DEBUG_LIST
config_enable CONFIG_DEBUG_PER_CPU_MAPS
config_set_val CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT 1
config_enable CONFIG_DEBUG_OBJECTS
config_enable CONFIG_DEBUG_OBJECTS_FREE
config_enable CONFIG_DEBUG_OBJECTS_TIMERS
config_enable CONFIG_DEBUG_OBJECTS_WORK
config_enable CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
config_enable CONFIG_DMA_API_DEBUG
## Lock debugging
config_enable CONFIG_DEBUG_LOCK_ALLOC
config_enable CONFIG_PROVE_LOCKING
config_enable CONFIG_LOCK_STAT
config_enable CONFIG_DEBUG_OBJECTS_RCU_HEAD
config_enable CONFIG_SPARSE_RCU_POINTER

> +             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;
> +}

Consider adding prestera_hw_switch_fini() that verifies that
'&sw->event_handlers' is empty.

> +
> +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->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_main.c 
> b/drivers/net/ethernet/marvell/prestera/prestera_main.c
> new file mode 100644
> index 000000000000..b5241e9b784a
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
> @@ -0,0 +1,506 @@
> +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> +/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/list.h>
> +#include <linux/netdevice.h>
> +#include <linux/netdev_features.h>
> +#include <linux/etherdevice.h>
> +#include <linux/jiffies.h>
> +#include <linux/of.h>
> +#include <linux/of_net.h>
> +
> +#include "prestera.h"
> +#include "prestera_hw.h"
> +#include "prestera_rxtx.h"
> +
> +#define PRESTERA_MTU_DEFAULT 1536
> +
> +#define PRESTERA_STATS_DELAY_MS      msecs_to_jiffies(1000)
> +
> +static struct workqueue_struct *prestera_wq;
> +
> +struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
> +                                              u32 dev_id, u32 hw_id)
> +{
> +     struct prestera_port *port;
> +
> +     rcu_read_lock();
> +
> +     list_for_each_entry_rcu(port, &sw->port_list, list) {
> +             if (port->dev_id == dev_id && port->hw_id == hw_id) {
> +                     rcu_read_unlock();
> +                     return port;

This does not look correct. You call rcu_read_unlock(), but do not take
a reference on the object, so nothing prevents it from being freed. 

> +             }
> +     }
> +
> +     rcu_read_unlock();
> +
> +     return NULL;
> +}
> +
> +static struct prestera_port *prestera_find_port(struct prestera_switch *sw,
> +                                             u32 port_id)
> +{
> +     struct prestera_port *port;
> +
> +     rcu_read_lock();
> +
> +     list_for_each_entry_rcu(port, &sw->port_list, list) {
> +             if (port->id == port_id)
> +                     break;
> +     }
> +
> +     rcu_read_unlock();
> +
> +     return port;

Same here.

> +}
> +
> +static int prestera_port_state_set(struct net_device *dev, bool is_up)
> +{
> +     struct prestera_port *port = netdev_priv(dev);
> +     int err;
> +
> +     if (!is_up)
> +             netif_stop_queue(dev);
> +
> +     err = prestera_hw_port_state_set(port, is_up);
> +
> +     if (is_up && !err)
> +             netif_start_queue(dev);
> +
> +     return err;
> +}
> +
> +static int prestera_port_open(struct net_device *dev)
> +{
> +     return prestera_port_state_set(dev, true);
> +}
> +
> +static int prestera_port_close(struct net_device *dev)
> +{
> +     return prestera_port_state_set(dev, false);
> +}
> +
> +static netdev_tx_t prestera_port_xmit(struct sk_buff *skb,
> +                                   struct net_device *dev)
> +{
> +     return prestera_rxtx_xmit(netdev_priv(dev), skb);
> +}
> +
> +static int prestera_is_valid_mac_addr(struct prestera_port *port, u8 *addr)
> +{
> +     if (!is_valid_ether_addr(addr))
> +             return -EADDRNOTAVAIL;
> +
> +     if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1))
> +             return -EINVAL;
> +
> +     return 0;
> +}
> +
> +static int prestera_port_set_mac_address(struct net_device *dev, void *p)
> +{
> +     struct prestera_port *port = netdev_priv(dev);
> +     struct sockaddr *addr = p;
> +     int err;
> +
> +     err = prestera_is_valid_mac_addr(port, addr->sa_data);
> +     if (err)
> +             return err;
> +
> +     err = prestera_hw_port_mac_set(port, addr->sa_data);
> +     if (err)
> +             return err;
> +
> +     memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
> +     return 0;
> +}
> +
> +static int prestera_port_change_mtu(struct net_device *dev, int mtu)
> +{
> +     struct prestera_port *port = netdev_priv(dev);
> +     int err;
> +
> +     err = prestera_hw_port_mtu_set(port, mtu);
> +     if (err)
> +             return err;
> +
> +     dev->mtu = mtu;
> +     return 0;
> +}
> +
> +static void prestera_port_get_stats64(struct net_device *dev,
> +                                   struct rtnl_link_stats64 *stats)
> +{
> +     struct prestera_port *port = netdev_priv(dev);
> +     struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
> +
> +     stats->rx_packets = port_stats->broadcast_frames_received +
> +                             port_stats->multicast_frames_received +
> +                             port_stats->unicast_frames_received;
> +
> +     stats->tx_packets = port_stats->broadcast_frames_sent +
> +                             port_stats->multicast_frames_sent +
> +                             port_stats->unicast_frames_sent;
> +
> +     stats->rx_bytes = port_stats->good_octets_received;
> +
> +     stats->tx_bytes = port_stats->good_octets_sent;
> +
> +     stats->rx_errors = port_stats->rx_error_frame_received;
> +     stats->tx_errors = port_stats->mac_trans_error;
> +
> +     stats->rx_dropped = port_stats->buffer_overrun;
> +     stats->tx_dropped = 0;
> +
> +     stats->multicast = port_stats->multicast_frames_received;
> +     stats->collisions = port_stats->excessive_collision;
> +
> +     stats->rx_crc_errors = port_stats->bad_crc;
> +}
> +
> +static void prestera_port_get_hw_stats(struct prestera_port *port)
> +{
> +     prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats);
> +}
> +
> +static void prestera_port_stats_update(struct work_struct *work)
> +{
> +     struct prestera_port *port =
> +             container_of(work, struct prestera_port,
> +                          cached_hw_stats.caching_dw.work);
> +
> +     prestera_port_get_hw_stats(port);
> +
> +     queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw,
> +                        PRESTERA_STATS_DELAY_MS);
> +}
> +
> +static const struct net_device_ops netdev_ops = {
> +     .ndo_open = prestera_port_open,
> +     .ndo_stop = prestera_port_close,
> +     .ndo_start_xmit = prestera_port_xmit,
> +     .ndo_change_mtu = prestera_port_change_mtu,
> +     .ndo_get_stats64 = prestera_port_get_stats64,
> +     .ndo_set_mac_address = prestera_port_set_mac_address,
> +};
> +
> +static int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
> +                                  u64 link_modes, u8 fec)
> +{
> +     bool refresh = false;
> +     int err = 0;
> +
> +     if (port->caps.type != PRESTERA_PORT_TYPE_TP)
> +             return enable ? -EINVAL : 0;
> +
> +     if (port->adver_link_modes != link_modes || port->adver_fec != fec) {
> +             port->adver_fec = fec ?: BIT(PRESTERA_PORT_FEC_OFF);
> +             port->adver_link_modes = link_modes;
> +             refresh = true;
> +     }
> +
> +     if (port->autoneg == enable && !(port->autoneg && refresh))
> +             return 0;
> +
> +     err = prestera_hw_port_autoneg_set(port, enable, port->adver_link_modes,
> +                                        port->adver_fec);
> +     if (err)
> +             return -EINVAL;
> +
> +     port->autoneg = enable;
> +     return 0;
> +}
> +
> +static int prestera_port_create(struct prestera_switch *sw, u32 id)
> +{
> +     struct prestera_port *port;
> +     struct net_device *dev;
> +     int err;
> +
> +     dev = alloc_etherdev(sizeof(*port));
> +     if (!dev)
> +             return -ENOMEM;
> +
> +     port = netdev_priv(dev);
> +
> +     port->dev = dev;
> +     port->id = id;
> +     port->sw = sw;
> +
> +     err = prestera_hw_port_info_get(port, &port->fp_id,
> +                                     &port->hw_id, &port->dev_id);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id);
> +             goto err_port_init;
> +     }
> +
> +     dev->features |= NETIF_F_NETNS_LOCAL;
> +     dev->netdev_ops = &netdev_ops;
> +
> +     netif_carrier_off(dev);
> +
> +     dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT);
> +     dev->min_mtu = sw->mtu_min;
> +     dev->max_mtu = sw->mtu_max;
> +
> +     err = prestera_hw_port_mtu_set(port, dev->mtu);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n",
> +                     id, dev->mtu);
> +             goto err_port_init;
> +     }
> +
> +     /* Only 0xFF mac addrs are supported */
> +     if (port->fp_id >= 0xFF)
> +             goto err_port_init;
> +
> +     memcpy(dev->dev_addr, sw->base_mac, dev->addr_len - 1);
> +     dev->dev_addr[dev->addr_len - 1] = (char)port->fp_id;
> +
> +     err = prestera_hw_port_mac_set(port, dev->dev_addr);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", 
> id);
> +             goto err_port_init;
> +     }
> +
> +     err = prestera_hw_port_cap_get(port, &port->caps);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id);
> +             goto err_port_init;
> +     }
> +
> +     port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
> +     prestera_port_autoneg_set(port, true, port->caps.supp_link_modes,
> +                               port->caps.supp_fec);
> +
> +     err = prestera_hw_port_state_set(port, false);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to set port(%u) down\n", id);
> +             goto err_port_init;
> +     }
> +
> +     err = prestera_rxtx_port_init(port);
> +     if (err)
> +             goto err_port_init;
> +
> +     INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw,
> +                       &prestera_port_stats_update);
> +
> +     list_add_rcu(&port->list, &sw->port_list);
> +
> +     err = register_netdev(dev);
> +     if (err)
> +             goto err_register_netdev;
> +
> +     return 0;
> +
> +err_register_netdev:
> +     list_del_rcu(&port->list);
> +err_port_init:
> +     free_netdev(dev);
> +     return err;
> +}
> +
> +static void prestera_port_destroy(struct prestera_port *port)
> +{
> +     struct net_device *dev = port->dev;
> +
> +     cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw);
> +     unregister_netdev(dev);
> +
> +     list_del_rcu(&port->list);
> +

I'm not sure what is the point of these blank lines. Best to remove
them.

> +     free_netdev(dev);
> +}
> +
> +static void prestera_destroy_ports(struct prestera_switch *sw)
> +{
> +     struct prestera_port *port, *tmp;
> +     struct list_head remove_list;
> +
> +     INIT_LIST_HEAD(&remove_list);
> +
> +     list_splice_init(&sw->port_list, &remove_list);
> +
> +     list_for_each_entry_safe(port, tmp, &remove_list, list)
> +             prestera_port_destroy(port);
> +}
> +
> +static int prestera_create_ports(struct prestera_switch *sw)
> +{
> +     u32 port;
> +     int err;
> +
> +     for (port = 0; port < sw->port_count; port++) {
> +             err = prestera_port_create(sw, port);
> +             if (err)
> +                     goto err_ports_init;

err_port_create ?

> +     }
> +
> +     return 0;
> +
> +err_ports_init:
> +     prestera_destroy_ports(sw);

I'm not a fan of this construct. I find it best to always do proper
rollback in the error path. Then you can always maintain init() being
followed by fini() which is much easier to review.

> +     return err;
> +}
> +
> +static void prestera_port_handle_event(struct prestera_switch *sw,
> +                                    struct prestera_event *evt, void *arg)
> +{
> +     struct delayed_work *caching_dw;
> +     struct prestera_port *port;
> +
> +     port = prestera_find_port(sw, evt->port_evt.port_id);
> +     if (!port)
> +             return;
> +
> +     caching_dw = &port->cached_hw_stats.caching_dw;
> +
> +     if (evt->id == PRESTERA_PORT_EVENT_STATE_CHANGED) {
> +             if (evt->port_evt.data.oper_state) {
> +                     netif_carrier_on(port->dev);
> +                     if (!delayed_work_pending(caching_dw))
> +                             queue_delayed_work(prestera_wq, caching_dw, 0);
> +             } else {
> +                     netif_carrier_off(port->dev);
> +                     if (delayed_work_pending(caching_dw))
> +                             cancel_delayed_work(caching_dw);
> +             }
> +     }
> +}
> +
> +static void prestera_event_handlers_unregister(struct prestera_switch *sw)
> +{
> +     prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT,
> +                                          prestera_port_handle_event);
> +}

Please reverse the order so that register() is first.

> +
> +static int prestera_event_handlers_register(struct prestera_switch *sw)
> +{
> +     return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT,
> +                                               prestera_port_handle_event,
> +                                               NULL);
> +}
> +
> +static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw)
> +{
> +     struct device_node *base_mac_np;
> +     struct device_node *np;
> +
> +     np = of_find_compatible_node(NULL, NULL, "marvell,prestera");
> +     if (np) {
> +             base_mac_np = of_parse_phandle(np, "base-mac-provider", 0);
> +             if (base_mac_np) {
> +                     const char *base_mac;
> +
> +                     base_mac = of_get_mac_address(base_mac_np);
> +                     of_node_put(base_mac_np);
> +                     if (!IS_ERR(base_mac))
> +                             ether_addr_copy(sw->base_mac, base_mac);
> +             }
> +     }
> +
> +     if (!is_valid_ether_addr(sw->base_mac)) {
> +             eth_random_addr(sw->base_mac);
> +             dev_info(sw->dev->dev, "using random base mac address\n");
> +     }
> +
> +     return prestera_hw_switch_mac_set(sw, sw->base_mac);
> +}
> +
> +static int prestera_switch_init(struct prestera_switch *sw)
> +{
> +     int err;
> +
> +     err = prestera_hw_switch_init(sw);
> +     if (err) {
> +             dev_err(prestera_dev(sw), "Failed to init Switch device\n");
> +             return err;
> +     }
> +
> +     INIT_LIST_HEAD(&sw->port_list);
> +
> +     err = prestera_switch_set_base_mac_addr(sw);
> +     if (err)
> +             return err;
> +
> +     err = prestera_rxtx_switch_init(sw);
> +     if (err)
> +             return err;
> +
> +     err = prestera_event_handlers_register(sw);
> +     if (err)
> +             return err;
> +
> +     err = prestera_create_ports(sw);
> +     if (err)
> +             goto err_ports_create;
> +
> +     return 0;
> +
> +err_ports_create:
> +     prestera_event_handlers_unregister(sw);
> +

You are missing prestera_rxtx_switch_fini() here... With init() always
followed by fini() you can easily tell that the error path is not
symmetric with fini().

> +     return err;
> +}
> +
> +static void prestera_switch_fini(struct prestera_switch *sw)
> +{
> +     prestera_destroy_ports(sw);
> +     prestera_event_handlers_unregister(sw);
> +     prestera_rxtx_switch_fini(sw);
> +}
> +
> +int prestera_device_register(struct prestera_device *dev)
> +{
> +     struct prestera_switch *sw;
> +     int err;
> +
> +     sw = kzalloc(sizeof(*sw), GFP_KERNEL);
> +     if (!sw)
> +             return -ENOMEM;
> +
> +     dev->priv = sw;
> +     sw->dev = dev;
> +
> +     err = prestera_switch_init(sw);
> +     if (err) {
> +             kfree(sw);
> +             return err;
> +     }
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(prestera_device_register);
> +
> +void prestera_device_unregister(struct prestera_device *dev)
> +{
> +     struct prestera_switch *sw = dev->priv;
> +
> +     prestera_switch_fini(sw);
> +     kfree(sw);
> +}
> +EXPORT_SYMBOL(prestera_device_unregister);
> +
> +static int __init prestera_module_init(void)
> +{
> +     prestera_wq = alloc_workqueue("prestera", 0, 0);
> +     if (!prestera_wq)
> +             return -ENOMEM;
> +
> +     return 0;
> +}
> +
> +static void __exit prestera_module_exit(void)
> +{
> +     destroy_workqueue(prestera_wq);
> +}
> +
> +module_init(prestera_module_init);
> +module_exit(prestera_module_exit);
> +
> +MODULE_LICENSE("Dual BSD/GPL");
> +MODULE_DESCRIPTION("Marvell Prestera switch driver");
> 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..78f1b7dfdc2e
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c
> @@ -0,0 +1,860 @@
> +// 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"
> +#include "prestera_rxtx.h"
> +
> +struct prestera_sdma_desc {
> +     __le32 word1;
> +     __le32 word2;
> +     __le32 buff;
> +     __le32 next;
> +} __packed __aligned(16);
> +
> +#define PRESTERA_SDMA_BUFF_SIZE_MAX  1544
> +
> +#define PRESTERA_SDMA_RX_DESC_PKT_LEN(desc) \
> +     ((le32_to_cpu((desc)->word2) >> 16) & 0x3FFF)
> +
> +#define PRESTERA_SDMA_RX_DESC_OWNER(desc) \
> +     ((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
> +
> +#define PRESTERA_SDMA_RX_DESC_IS_RCVD(desc) \
> +     (PRESTERA_SDMA_RX_DESC_OWNER((desc)) == PRESTERA_SDMA_RX_DESC_CPU_OWN)
> +
> +#define PRESTERA_SDMA_RX_DESC_CPU_OWN        0
> +#define PRESTERA_SDMA_RX_DESC_DMA_OWN        1
> +
> +#define PRESTERA_SDMA_RX_QUEUE_NUM   8
> +
> +#define PRESTERA_SDMA_RX_DESC_PER_Q  1000
> +
> +#define PRESTERA_SDMA_TX_DESC_PER_Q  1000
> +#define PRESTERA_SDMA_TX_MAX_BURST   64
> +
> +#define PRESTERA_SDMA_TX_DESC_OWNER(desc) \
> +     ((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
> +
> +#define PRESTERA_SDMA_TX_DESC_CPU_OWN        0
> +#define PRESTERA_SDMA_TX_DESC_DMA_OWN        1
> +
> +#define PRESTERA_SDMA_TX_DESC_IS_SENT(desc) \
> +     (PRESTERA_SDMA_TX_DESC_OWNER(desc) == PRESTERA_SDMA_TX_DESC_CPU_OWN)
> +
> +#define PRESTERA_SDMA_TX_DESC_LAST   BIT(20)
> +#define PRESTERA_SDMA_TX_DESC_FIRST  BIT(21)
> +#define PRESTERA_SDMA_TX_DESC_CALC_CRC       BIT(12)
> +
> +#define PRESTERA_SDMA_TX_DESC_SINGLE \
> +     (PRESTERA_SDMA_TX_DESC_FIRST | PRESTERA_SDMA_TX_DESC_LAST)
> +
> +#define PRESTERA_SDMA_TX_DESC_INIT   \
> +     (PRESTERA_SDMA_TX_DESC_SINGLE | PRESTERA_SDMA_TX_DESC_CALC_CRC)
> +
> +#define PRESTERA_SDMA_RX_INTR_MASK_REG               0x2814
> +#define PRESTERA_SDMA_RX_QUEUE_STATUS_REG    0x2680
> +#define PRESTERA_SDMA_RX_QUEUE_DESC_REG(n)   (0x260C + (n) * 16)
> +
> +#define PRESTERA_SDMA_TX_QUEUE_DESC_REG              0x26C0
> +#define PRESTERA_SDMA_TX_QUEUE_START_REG             0x2868
> +
> +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[PRESTERA_SDMA_RX_QUEUE_NUM];
> +     struct prestera_tx_ring tx_ring;
> +     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;
> +     /* protect SDMA with concurrrent access from multiple CPUs */
> +     spinlock_t tx_lock;
> +};
> +
> +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, PRESTERA_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(PRESTERA_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 prestera_sdma *sdma,
> +                                  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(sdma->sw, 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_next_rx_buf_idx(int buf_idx)
> +{
> +     return (buf_idx + 1) % PRESTERA_SDMA_RX_DESC_PER_Q;
> +}
> +
> +static int prestera_sdma_rx_poll(struct napi_struct *napi, int budget)
> +{
> +     int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
> +     unsigned int rxq_done_map = 0;
> +     struct prestera_sdma *sdma;
> +     struct list_head rx_list;
> +     unsigned int qmask;
> +     int pkts_done = 0;
> +     int q;
> +
> +     qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
> +     qmask = GENMASK(qnum - 1, 0);
> +
> +     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 < qnum && pkts_done < budget; q++) {
> +                     struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> +                     struct prestera_sdma_desc *desc;
> +                     struct prestera_sdma_buf *buf;
> +                     int buf_idx = ring->next_rx;
> +                     struct sk_buff *skb;
> +
> +                     buf = &ring->bufs[buf_idx];
> +                     desc = buf->desc;
> +
> +                     if (PRESTERA_SDMA_RX_DESC_IS_RCVD(desc)) {
> +                             rxq_done_map &= ~BIT(q);
> +                     } else {
> +                             rxq_done_map |= BIT(q);
> +                             continue;
> +                     }
> +
> +                     pkts_done++;
> +
> +                     __skb_trim(buf->skb, 
> PRESTERA_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(sdma, skb)))
> +                             goto rx_next_buf;
> +
> +                     list_add_tail(&skb->list, &rx_list);
> +rx_next_buf:
> +                     ring->next_rx = prestera_sdma_next_rx_buf_idx(buf_idx);
> +             }
> +     }
> +
> +     if (pkts_done < budget && napi_complete_done(napi, pkts_done))
> +             prestera_write(sdma->sw, PRESTERA_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 qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
> +     int q, b;
> +
> +     /* disable all rx queues */
> +     prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG, 0xff00);
> +
> +     for (q = 0; q < qnum; q++) {
> +             struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> +
> +             if (!ring->bufs)
> +                     break;
> +
> +             for (b = 0; b < PRESTERA_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 bnum = PRESTERA_SDMA_RX_DESC_PER_Q;
> +     int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
> +     int q, b;
> +     int err;
> +
> +     /* disable all rx queues */
> +     prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG, 0xff00);
> +
> +     for (q = 0; q < qnum; q++) {
> +             struct prestera_rx_ring *ring = &sdma->rx_ring[q];
> +             struct prestera_sdma_buf *head;
> +
> +             ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
> +             if (!ring->bufs)
> +                     return -ENOMEM;
> +
> +             head = &ring->bufs[0];
> +             ring->next_rx = 0;
> +
> +             for (b = 0; b < bnum; 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 == PRESTERA_SDMA_RX_DESC_PER_Q - 1)
> +                             prestera_sdma_rx_desc_set_next(sdma, buf->desc,
> +                                                            head->desc_dma);
> +             }
> +
> +             prestera_write(sdma->sw, PRESTERA_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, PRESTERA_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(PRESTERA_SDMA_TX_DESC_INIT);
> +     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 |= PRESTERA_SDMA_TX_DESC_DMA_OWN << 31;
> +
> +     /* 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)
> +{
> +     int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
> +     struct prestera_tx_ring *tx_ring;
> +     struct prestera_sdma *sdma;
> +     int b;
> +
> +     sdma = container_of(work, struct prestera_sdma, tx_work);
> +
> +     tx_ring = &sdma->tx_ring;
> +
> +     for (b = 0; b < bnum; b++) {
> +             struct prestera_sdma_buf *buf = &tx_ring->bufs[b];
> +
> +             if (!buf->is_used)
> +                     continue;
> +
> +             if (!PRESTERA_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;
> +     int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
> +     struct prestera_sdma_buf *head;
> +     int err;
> +     int b;
> +
> +     INIT_WORK(&sdma->tx_work, prestera_sdma_tx_recycle_work_fn);
> +     spin_lock_init(&sdma->tx_lock);
> +
> +     tx_ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
> +     if (!tx_ring->bufs)
> +             return -ENOMEM;
> +
> +     head = &tx_ring->bufs[0];
> +
> +     tx_ring->max_burst = PRESTERA_SDMA_TX_MAX_BURST;
> +     tx_ring->burst = tx_ring->max_burst;
> +     tx_ring->next_tx = 0;
> +
> +     for (b = 0; b < bnum; 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 == PRESTERA_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, PRESTERA_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 bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
> +     int b;
> +
> +     cancel_work_sync(&sdma->tx_work);
> +
> +     if (!ring->bufs)
> +             return;
> +
> +     for (b = 0; b < bnum; 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, PRESTERA_SDMA_RX_INTR_MASK_REG, 0);
> +     napi_schedule(&sdma->rx_napi);
> +}
> +
> +static 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;
> +}
> +
> +static 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);

Does not look like the order here is correct.

> +}
> +
> +static bool prestera_sdma_is_ready(struct prestera_sdma *sdma)
> +{
> +     return !(prestera_read(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG) & 1);
> +}
> +
> +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_sdma_is_ready(sdma))
> +                     return 0;
> +
> +             udelay(1);
> +     }
> +
> +     return -EBUSY;
> +}
> +
> +static void prestera_sdma_tx_start(struct prestera_sdma *sdma)
> +{
> +     prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG, 1);
> +     schedule_work(&sdma->tx_work);
> +}
> +
> +static netdev_tx_t prestera_sdma_xmit(struct prestera_sdma *sdma,
> +                                   struct sk_buff *skb)
> +{
> +     struct device *dma_dev = sdma->sw->dev->dev;
> +     struct net_device *dev = skb->dev;
> +     struct prestera_tx_ring *tx_ring;
> +     struct prestera_sdma_buf *buf;
> +     int err;
> +
> +     spin_lock(&sdma->tx_lock);
> +
> +     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;
> +     }
> +
> +     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) % PRESTERA_SDMA_TX_DESC_PER_Q;
> +     prestera_sdma_tx_desc_xmit(buf->desc);
> +     buf->is_used = true;
> +
> +     prestera_sdma_tx_start(sdma);
> +
> +     goto tx_done;
> +
> +drop_skb_unmap:
> +     prestera_sdma_tx_buf_unmap(sdma, buf);
> +drop_skb:
> +     dev_consume_skb_any(skb);
> +drop_skb_nofree:
> +     dev->stats.tx_dropped++;
> +tx_done:
> +     spin_unlock(&sdma->tx_lock);
> +     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;

Why do you need headroom for FCS?

> +     return 0;
> +}
> +
> +netdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff 
> *skb)
> +{
> +     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
> 

Reply via email to