netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver
@ 2023-11-15 13:34 Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 01/13] net:ethernet:realtek:rtase: Add pci table supported in this module Justin Lai
                   ` (13 more replies)
  0 siblings, 14 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

This series includes adding realtek automotive ethernet driver 
and adding rtase ethernet driver entry in MAINTAINERS file.

This ethernet device driver for the PCIe interface of 
Realtek Automotive Ethernet Switch,applicable to 
RTL9054, RTL9068, RTL9072, RTL9075, RTL9068, RTL9071.

v1 -> v2:
- Remove redundent debug message.
- Modify coding rule.
- Remove other function codes not related to netdev.

v2 -> v3:
- Remove SR-IOV function - We will add the SR-IOV function together when
uploading the vf driver in the future.
- Remove other unnecessary code and macro.

v3 -> v4:
- Remove function prototype - Our driver does not use recursion, so we
have reordered the code and removed the function prototypes.
- Define macro precisely - Improve macro code readability to make the
source code cleaner.

v4 -> v5:
- Modify ethtool function - Remove some unnecessary code.
- Don't use inline function - Let the compiler decide.

v5 -> v6:
- Some old macro definitions have been removed and replaced with the
lastest usage.
- Replace s32 with int to ensure consistency.
- Clearly point out the objects of the service and remove unnecessary
struct.

v6 -> v7:
- Split this driver into multiple patches.
- Reorganize this driver code and remove redundant code to make this
driver more concise.

v7 -> v8:
- Add the function to calculate time mitigation and the function to 
calculate packet number mitigation. Users can use these two functions 
to calculate the reg value that needs to be set for the mitigation value
they want to set.
- This device is usually used in automotive embedded systems. The page
pool api will use more memory in receiving packets and requires more 
verification, so we currently do not plan to use it in this patch.

v8 -> v9:
- Declare functions that are not extern as static functions and increase
the size of the character array named name in the rtase_int_vector struct
to correct the build warning noticed by the kernel test robot.

v9 -> v10:
- Currently we change to use the page pool api. However, when we allocate
more than one page to an rx buffer, it will cause system errors
in some cases. Therefore, we set the rx buffer to fixed size with 3776
(PAGE_SIZE - SKB_DATA_ALIGN(sizeof(skb_shared_info) )), and the maximum 
value of mtu is set to 3754(rx buffer size - VLAN_ETH_HLEN - ETH_FCS_LEN).
- When ndo_tx_timeout is called, it will dump some device information,
which can be used for debugging.
- When the mtu is greater than 1500, the device supports checksums
but not TSO.
- Fix compiler warnning.

v10 -> v11:
- Added error handling of rtase_init_ring().
- Modify the error related to asymmetric pause in rtase_get_settings.
- Fix compiler error.

Justin Lai (13):
  net:ethernet:realtek:rtase: Add pci table supported in this module
  net:ethernet:realtek:rtase: Implement the .ndo_open function
  net:ethernet:realtek:rtase: Implement the rtase_down function
  net:ethernet:realtek:rtase: Implement the interrupt routine and
    rtase_poll
  net:ethernet:realtek:rtase: Implement hardware configuration function
  net:ethernet:realtek:rtase: Implement .ndo_start_xmit function
  net:ethernet:realtek:rtase: Implement a function to receive packets
  net:ethernet:realtek:rtase: Implement net_device_ops
  net:ethernet:realtek:rtase: Implement pci_driver suspend and resume
    function
  net:ethernet:realtek:rtase: Implement ethtool function
  net:ethernet:realtek:rtase: Add a Makefile in the rtase folder
  net:ethernet:realtek: Update the Makefile and Kconfig in the realtek
    folder
  MAINTAINERS: Add the rtase ethernet driver entry

 MAINTAINERS                                   |    7 +
 drivers/net/ethernet/realtek/Kconfig          |   17 +
 drivers/net/ethernet/realtek/Makefile         |    1 +
 drivers/net/ethernet/realtek/rtase/Makefile   |   10 +
 drivers/net/ethernet/realtek/rtase/rtase.h    |  353 +++
 .../net/ethernet/realtek/rtase/rtase_main.c   | 2542 +++++++++++++++++
 drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
 drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++
 8 files changed, 5825 insertions(+)
 create mode 100644 drivers/net/ethernet/realtek/rtase/Makefile
 create mode 100644 drivers/net/ethernet/realtek/rtase/rtase.h
 create mode 100644 drivers/net/ethernet/realtek/rtase/rtase_main.c
 create mode 100644 drivers/net/ethernet/realtek/rtase/tt.c
 create mode 100644 drivers/net/ethernet/realtek/rtase/tt.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 01/13] net:ethernet:realtek:rtase: Add pci table supported in this module
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 02/13] net:ethernet:realtek:rtase: Implement the .ndo_open function Justin Lai
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Add pci table supported in this module, and implement pci_driver function
to initialize this driver, remove this driver, or shutdown this driver.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 drivers/net/ethernet/realtek/rtase/rtase.h    | 353 ++++++++++
 .../net/ethernet/realtek/rtase/rtase_main.c   | 622 ++++++++++++++++++
 2 files changed, 975 insertions(+)
 create mode 100644 drivers/net/ethernet/realtek/rtase/rtase.h
 create mode 100644 drivers/net/ethernet/realtek/rtase/rtase_main.c

diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h
new file mode 100644
index 000000000000..9239c518c504
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/rtase.h
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ *  rtase is the Linux device driver released for Realtek Automotive Switch
+ *  controllers with PCI-Express interface.
+ *
+ *  Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef _RTASE_H_
+#define _RTASE_H_
+
+/* the low 32 bit address of receive buffer must be 8-byte alignment. */
+#define RTK_RX_ALIGN 8
+
+#define HW_VER_MASK 0x7C800000
+
+#define RX_DMA_BURST_256       4
+#define TX_DMA_BURST_UNLIMITED 7
+#define RX_BUF_SIZE            (PAGE_SIZE - \
+				SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+#define MAX_JUMBO_SIZE         (RX_BUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN)
+
+/* 3 means InterFrameGap = the shortest one */
+#define INTERFRAMEGAP 0x03
+
+#define RTASE_REGS_SIZE     256
+#define RTASE_PCI_REGS_SIZE 0x100
+
+#define MULTICAST_FILTER_MASK  GENMASK(30, 26)
+#define MULTICAST_FILTER_LIMIT 32
+
+#define RTASE_VLAN_FILTER_ENTRY_NUM 32
+#define RTASE_NUM_TX_QUEUE 8
+#define RTASE_NUM_RX_QUEUE 4
+
+#define RTASE_TXQ_CTRL      1
+#define RTASE_FUNC_TXQ_NUM  1
+#define RTASE_FUNC_RXQ_NUM  1
+#define RTASE_INTERRUPT_NUM 1
+
+#define MITI_TIME_COUNT_MASK     GENMASK(3, 0)
+#define MITI_TIME_UNIT_MASK      GENMASK(7, 4)
+#define MITI_DEFAULT_TIME        128
+#define MITI_MAX_TIME            491520
+#define MITI_PKT_NUM_COUNT_MASK  GENMASK(11, 8)
+#define MITI_PKT_NUM_UNIT_MASK   GENMASK(13, 12)
+#define MITI_DEFAULT_PKT_NUM     64
+#define MITI_MAX_PKT_NUM_IDX     3
+#define MITI_MAX_PKT_NUM_UNIT    16
+#define MITI_MAX_PKT_NUM         240
+#define MITI_COUNT_BIT_NUM       4
+
+#define RTASE_NUM_MSIX 4
+
+#define RTASE_DWORD_MOD 16
+
+/*****************************************************************************/
+enum rtase_registers {
+	RTASE_MAC0   = 0x0000,
+	RTASE_MAC4   = 0x0004,
+	RTASE_MAR0   = 0x0008,
+	RTASE_MAR1   = 0x000C,
+	RTASE_DTCCR0 = 0x0010,
+	RTASE_DTCCR4 = 0x0014,
+#define COUNTER_RESET BIT(0)
+#define COUNTER_DUMP  BIT(3)
+
+	RTASE_FCR    = 0x0018,
+#define FCR_RXQ_MASK    GENMASK(5, 4)
+#define FCR_VLAN_FTR_EN BIT(1)
+
+	RTASE_LBK_CTRL = 0x001A,
+#define LBK_ATLD BIT(1)
+#define LBK_CLR  BIT(0)
+
+	RTASE_TX_DESC_ADDR0   = 0x0020,
+	RTASE_TX_DESC_ADDR4   = 0x0024,
+	RTASE_TX_DESC_COMMAND = 0x0028,
+#define TX_DESC_CMD_CS BIT(15)
+#define TX_DESC_CMD_WE BIT(14)
+
+	RTASE_BOOT_CTL  = 0x6004,
+	RTASE_CLKSW_SET = 0x6018,
+
+	RTASE_CHIP_CMD = 0x0037,
+#define STOP_REQ      BIT(7)
+#define STOP_REQ_DONE BIT(6)
+#define RE            BIT(3)
+#define TE            BIT(2)
+
+	RTASE_IMR0 = 0x0038,
+	RTASE_ISR0 = 0x003C,
+#define TOK7 BIT(30)
+#define TOK6 BIT(28)
+#define TOK5 BIT(26)
+#define TOK4 BIT(24)
+#define FOVW BIT(6)
+#define RDU  BIT(4)
+#define TOK  BIT(2)
+#define ROK  BIT(0)
+
+	RTASE_IMR1 = 0x0800,
+	RTASE_ISR1 = 0x0802,
+#define Q_TOK BIT(4)
+#define Q_RDU BIT(1)
+#define Q_ROK BIT(0)
+
+	RTASE_EPHY_ISR = 0x6014,
+	RTASE_EPHY_IMR = 0x6016,
+
+	RTASE_TX_CONFIG_0 = 0x0040,
+#define TX_INTER_FRAME_GAP_MASK GENMASK(25, 24)
+	/* DMA burst value (0-7) is shift this many bits */
+#define TX_DMA_MASK             GENMASK(10, 8)
+
+	RTASE_RX_CONFIG_0 = 0x0044,
+#define RX_SINGLE_FETCH  BIT(14)
+#define RX_SINGLE_TAG    BIT(13)
+#define RX_MX_DMA_MASK   GENMASK(10, 8)
+#define ACPT_FLOW        BIT(7)
+#define ACCEPT_ERR       BIT(5)
+#define ACCEPT_RUNT      BIT(4)
+#define ACCEPT_BROADCAST BIT(3)
+#define ACCEPT_MULTICAST BIT(2)
+#define ACCEPT_MYPHYS    BIT(1)
+#define ACCEPT_ALLPHYS   BIT(0)
+#define ACCEPT_MASK      (ACPT_FLOW | ACCEPT_ERR | ACCEPT_RUNT | \
+			  ACCEPT_BROADCAST | ACCEPT_MULTICAST | \
+			  ACCEPT_MYPHYS | ACCEPT_ALLPHYS)
+
+	RTASE_RX_CONFIG_1 = 0x0046,
+#define RX_MAX_FETCH_DESC_MASK  GENMASK(15, 11)
+#define RX_NEW_DESC_FORMAT_EN   BIT(8)
+#define OUTER_VLAN_DETAG_EN     BIT(7)
+#define INNER_VLAN_DETAG_EN     BIT(6)
+#define PCIE_NEW_FLOW           BIT(2)
+#define PCIE_RELOAD_En          BIT(0)
+
+	RTASE_EEM = 0x0050,
+#define EEM_UNLOCK 0xC0
+
+	RTASE_TDFNR  = 0x0057,
+	RTASE_TPPOLL = 0x0090,
+	RTASE_PDR    = 0x00B0,
+	RTASE_FIFOR  = 0x00D3,
+#define TX_FIFO_EMPTY BIT(5)
+#define RX_FIFO_EMPTY BIT(4)
+
+	RTASE_PCPR = 0x00D8,
+#define PCPR_VLAN_FTR_EN BIT(6)
+
+	RTASE_RMS       = 0x00DA,
+	RTASE_CPLUS_CMD = 0x00E0,
+#define FORCE_RXFLOW_EN BIT(11)
+#define FORCE_TXFLOW_EN BIT(10)
+#define RX_CHKSUM       BIT(5)
+
+	RTASE_Q0_RX_DESC_ADDR0 = 0x00E4,
+	RTASE_Q0_RX_DESC_ADDR4 = 0x00E8,
+	RTASE_Q1_RX_DESC_ADDR0 = 0x4000,
+	RTASE_Q1_RX_DESC_ADDR4 = 0x4004,
+	RTASE_MTPS             = 0x00EC,
+#define TAG_NUM_SEL_MASK  GENMASK(10, 8)
+
+	RTASE_MISC = 0x00F2,
+#define RX_DV_GATE_EN BIT(3)
+
+	RTASE_TFUN_CTRL = 0x0400,
+#define TX_NEW_DESC_FORMAT_EN BIT(0)
+
+	RTASE_TX_CONFIG_1 = 0x203E,
+#define TC_MODE_MASK  GENMASK(11, 10)
+
+	RTASE_TOKSEL      = 0x2046,
+	RTASE_TXQCRDT_0   = 0x2500,
+	RTASE_RFIFONFULL  = 0x4406,
+	RTASE_INT_MITI_TX = 0x0A00,
+	RTASE_INT_MITI_RX = 0x0A80,
+
+	RTASE_VLAN_ENTRY_MEM_0 = 0x7234,
+	RTASE_VLAN_ENTRY_0     = 0xAC80,
+};
+
+enum desc_status_bit {
+	DESC_OWN = BIT(31), /* Descriptor is owned by NIC */
+	RING_END = BIT(30), /* End of descriptor ring */
+};
+
+enum sw_flag_content {
+	SWF_MSI_ENABLED  = BIT(1),
+	SWF_MSIX_ENABLED = BIT(2),
+};
+
+#define RSVD_MASK 0x3FFFC000
+
+struct tx_desc {
+	__le32 opts1;
+	__le32 opts2;
+	__le64 addr;
+	__le32 opts3;
+	__le32 reserved1;
+	__le32 reserved2;
+	__le32 reserved3;
+} __packed;
+
+/*------ offset 0 of tx descriptor ------*/
+#define TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */
+#define TX_LAST_FRAG  BIT(28) /* Tx Final segment of a packet */
+#define GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */
+#define GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */
+#define TX_VLAN_TAG   BIT(17) /* Add VLAN tag */
+
+/*------ offset 4 of tx descriptor ------*/
+#define TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */
+#define TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */
+#define TX_IPCS_C  BIT(29) /* Calculate IP checksum */
+#define TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */
+
+union rx_desc {
+	struct {
+		__le64 header_buf_addr;
+		__le32 reserved1;
+		__le32 opts_header_len;
+		__le64 addr;
+		__le32 reserved2;
+		__le32 opts1;
+	} __packed desc_cmd;
+
+	struct {
+		__le32 reserved1;
+		__le32 reserved2;
+		__le32 rss;
+		__le32 opts4;
+		__le32 reserved3;
+		__le32 opts3;
+		__le32 opts2;
+		__le32 opts1;
+	} __packed desc_status;
+} __packed;
+
+/*------ offset 28 of rx descriptor ------*/
+#define RX_FIRST_FRAG    BIT(25) /* Rx First segment of a packet */
+#define RX_LAST_FRAG     BIT(24) /* Rx Final segment of a packet */
+#define RX_RES           BIT(20)
+#define RX_RUNT          BIT(19)
+#define RX_RWT           BIT(18)
+#define RX_CRC           BIT(16)
+#define RX_V6F           BIT(31)
+#define RX_V4F           BIT(30)
+#define RX_UDPT          BIT(29)
+#define RX_TCPT          BIT(28)
+#define RX_IPF           BIT(26) /* IP checksum failed */
+#define RX_UDPF          BIT(25) /* UDP/IP checksum failed */
+#define RX_TCPF          BIT(24) /* TCP/IP checksum failed */
+#define RX_LBK_FIFO_FULL BIT(17) /* Loopback FIFO Full */
+#define RX_VLAN_TAG      BIT(16) /* VLAN tag available */
+
+#define NUM_DESC                1024
+#define RTASE_TX_RING_DESC_SIZE (NUM_DESC * sizeof(struct tx_desc))
+#define RTASE_RX_RING_DESC_SIZE (NUM_DESC * sizeof(union rx_desc))
+#define VLAN_ENTRY_CAREBIT      0xF0000000
+#define VLAN_TAG_MASK           GENMASK(15, 0)
+#define RX_PKT_SIZE_MASK        GENMASK(13, 0)
+
+/* txqos hardware definitions */
+#define RTASE_1T_CLOCK            64
+#define RTASE_1T_POWER            10000000
+#define RTASE_IDLESLOPE_INT_SHIFT 25
+#define RTASE_IDLESLOPE_INT_MASK  GENMASK(31, 25)
+
+#define IVEC_NAME_SIZE (IFNAMSIZ + 10)
+
+struct rtase_int_vector {
+	struct rtase_private *tp;
+	unsigned int irq;
+	u8 status;
+	char name[IVEC_NAME_SIZE];
+	u16 index;
+	u16 imr_addr;
+	u16 isr_addr;
+	u32 imr;
+	struct list_head ring_list;
+	struct napi_struct napi;
+	int (*poll)(struct napi_struct *napi, int budget);
+};
+
+struct rtase_ring {
+	struct rtase_int_vector *ivec;
+	void *desc;
+	dma_addr_t phy_addr;
+	u32 cur_idx;
+	u32 dirty_idx;
+	u16 index;
+
+	struct sk_buff *skbuff[NUM_DESC];
+	union {
+		u32 len[NUM_DESC];
+		dma_addr_t data_phy_addr[NUM_DESC];
+	} mis;
+
+	struct list_head ring_entry;
+	int (*ring_handler)(struct rtase_ring *ring, int budget);
+};
+
+struct rtase_txqos {
+	int hicredit;
+	int locredit;
+	int idleslope;
+	int sendslope;
+};
+
+struct rtase_private {
+	void __iomem *mmio_addr;
+	u32 sw_flag;
+	u32 mc_filter[2];
+
+	struct pci_dev *pdev;
+	struct net_device *dev;
+	u32 rx_buf_sz;
+
+	struct page_pool *page_pool;
+	struct rtase_ring tx_ring[RTASE_NUM_TX_QUEUE];
+	struct rtase_txqos tx_qos[RTASE_NUM_TX_QUEUE];
+	struct rtase_ring rx_ring[RTASE_NUM_RX_QUEUE];
+	struct rtase_counters *tally_vaddr;
+	dma_addr_t tally_paddr;
+
+	u32 vlan_filter_ctrl;
+	u16 vlan_filter_vid[RTASE_VLAN_FILTER_ENTRY_NUM];
+
+	struct delayed_work task;
+	u8 org_mac_addr[ETH_ALEN];
+	struct msix_entry msix_entry[RTASE_NUM_MSIX];
+	struct rtase_int_vector int_vector[RTASE_NUM_MSIX];
+
+	u16 tx_queue_ctrl;
+	u16 func_tx_queue_num;
+	u16 func_rx_queue_num;
+	u16 int_nums;
+	u16 tx_int_mit;
+	u16 rx_int_mit;
+};
+
+#define LSO_64K 64000
+
+#define NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4)
+
+#define TCPHO_MASK GENMASK(24, 18)
+
+#define MSS_MAX  0x07FF /* MSS value */
+#define MSS_MASK GENMASK(28, 18)
+
+#endif /* _RTASE_H_ */
diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
new file mode 100644
index 000000000000..6632a30da6ff
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ *  rtase is the Linux device driver released for Realtek Automotive Switch
+ *  controllers with PCI-Express interface.
+ *
+ *  Copyright(c) 2023 Realtek Semiconductor Corp.
+ *
+ *  Below is a simplified block diagram of the chip and its relevant interfaces.
+ *
+ *               *************************
+ *               *                       *
+ *               *  CPU network device   *
+ *               *                       *
+ *               *   +-------------+     *
+ *               *   |  PCIE Host  |     *
+ *               ***********++************
+ *                          ||
+ *                         PCIE
+ *                          ||
+ *      ********************++**********************
+ *      *            | PCIE Endpoint |             *
+ *      *            +---------------+             *
+ *      *                | GMAC |                  *
+ *      *                +--++--+  Realtek         *
+ *      *                   ||     RTL90xx Series  *
+ *      *                   ||                     *
+ *      *     +-------------++----------------+    *
+ *      *     |           | MAC |             |    *
+ *      *     |           +-----+             |    *
+ *      *     |                               |    *
+ *      *     |     Ethernet Switch Core      |    *
+ *      *     |                               |    *
+ *      *     |   +-----+           +-----+   |    *
+ *      *     |   | MAC |...........| MAC |   |    *
+ *      *     +---+-----+-----------+-----+---+    *
+ *      *         | PHY |...........| PHY |        *
+ *      *         +--++-+           +--++-+        *
+ *      *************||****************||***********
+ *
+ *  The block of the Realtek RTL90xx series is our entire chip architecture,
+ *  the GMAC is connected to the switch core, and there is no PHY in between.
+ *  In addition, this driver is mainly used to control GMAC, but does not
+ *  control the switch core, so it is not the same as DSA.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/prefetch.h>
+#include <linux/rtnetlink.h>
+#include <linux/tcp.h>
+#include <asm/irq.h>
+#include <net/ip6_checksum.h>
+#include <net/page_pool/helpers.h>
+#include <net/pkt_cls.h>
+
+#include "rtase.h"
+
+#define RTK_OPTS1_DEBUG_VALUE 0x0BADBEEF
+#define RTK_MAGIC_NUMBER      0x0BADBADBADBADBAD
+
+static const struct pci_device_id rtase_pci_tbl[] = {
+	{PCI_VDEVICE(REALTEK, 0x906A)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, rtase_pci_tbl);
+
+MODULE_AUTHOR("Realtek ARD Software Team");
+MODULE_DESCRIPTION("Network Driver for the PCIe interface of Realtek Automotive Ethernet Switch");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct rtase_counters {
+	__le64 tx_packets;
+	__le64 rx_packets;
+	__le64 tx_errors;
+	__le32 rx_errors;
+	__le16 rx_missed;
+	__le16 align_errors;
+	__le32 tx_one_collision;
+	__le32 tx_multi_collision;
+	__le64 rx_unicast;
+	__le64 rx_broadcast;
+	__le32 rx_multicast;
+	__le16 tx_aborted;
+	__le16 tx_underun;
+} __packed;
+
+static void rtase_w8(const struct rtase_private *tp, u16 reg, u8 val8)
+{
+	writeb(val8, tp->mmio_addr + reg);
+}
+
+static void rtase_w16(const struct rtase_private *tp, u16 reg, u16 val16)
+{
+	writew(val16, tp->mmio_addr + reg);
+}
+
+static void rtase_w32(const struct rtase_private *tp, u16 reg, u32 val32)
+{
+	writel(val32, tp->mmio_addr + reg);
+}
+
+static u8 rtase_r8(const struct rtase_private *tp, u16 reg)
+{
+	return readb(tp->mmio_addr + reg);
+}
+
+static u16 rtase_r16(const struct rtase_private *tp, u16 reg)
+{
+	return readw(tp->mmio_addr + reg);
+}
+
+static u32 rtase_r32(const struct rtase_private *tp, u16 reg)
+{
+	return readl(tp->mmio_addr + reg);
+}
+
+static void rtase_tally_counter_clear(const struct rtase_private *tp)
+{
+	u32 cmd = lower_32_bits(tp->tally_paddr);
+
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
+	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
+}
+
+static void rtase_enable_eem_write(const struct rtase_private *tp)
+{
+	u8 val;
+
+	val = rtase_r8(tp, RTASE_EEM);
+	rtase_w8(tp, RTASE_EEM, val | EEM_UNLOCK);
+}
+
+static void rtase_disable_eem_write(const struct rtase_private *tp)
+{
+	u8 val;
+
+	val = rtase_r8(tp, RTASE_EEM);
+	rtase_w8(tp, RTASE_EEM, val & ~EEM_UNLOCK);
+}
+
+static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
+{
+	u32 rar_low, rar_high;
+
+	rar_low = (u32)addr[0] | ((u32)addr[1] << 8) |
+		  ((u32)addr[2] << 16) | ((u32)addr[3] << 24);
+
+	rar_high = (u32)addr[4] | ((u32)addr[5] << 8);
+
+	rtase_enable_eem_write(tp);
+	rtase_w32(tp, RTASE_MAC0, rar_low);
+	rtase_w32(tp, RTASE_MAC4, rar_high);
+	rtase_disable_eem_write(tp);
+	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
+}
+
+static void rtase_get_mac_address(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u8 mac_addr[ETH_ALEN] __aligned(2) = {};
+	u32 i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		mac_addr[i] = rtase_r8(tp, RTASE_MAC0 + i);
+
+	if (!is_valid_ether_addr(mac_addr)) {
+		eth_random_addr(mac_addr);
+		dev->addr_assign_type = NET_ADDR_RANDOM;
+		netdev_warn(dev, "Random ether addr %pM\n", mac_addr);
+	}
+
+	eth_hw_addr_set(dev, mac_addr);
+	rtase_rar_set(tp, mac_addr);
+
+	/* keep the original MAC address */
+	ether_addr_copy(tp->org_mac_addr, dev->dev_addr);
+	ether_addr_copy(dev->perm_addr, dev->dev_addr);
+}
+
+static void rtase_reset_interrupt(struct pci_dev *pdev,
+				  const struct rtase_private *tp)
+{
+	if (tp->sw_flag & SWF_MSIX_ENABLED)
+		pci_disable_msix(pdev);
+	else
+		pci_disable_msi(pdev);
+}
+
+static int rtase_alloc_msix(struct pci_dev *pdev, struct rtase_private *tp)
+{
+	int ret;
+	u16 i;
+
+	memset(tp->msix_entry, 0x0, RTASE_NUM_MSIX * sizeof(struct msix_entry));
+
+	for (i = 0; i < RTASE_NUM_MSIX; i++)
+		tp->msix_entry[i].entry = i;
+
+	ret = pci_enable_msix_range(pdev, tp->msix_entry, tp->int_nums,
+				    tp->int_nums);
+
+	if (ret == tp->int_nums) {
+		for (i = 0; i < tp->int_nums; i++) {
+			tp->int_vector[i].irq = pci_irq_vector(pdev, i);
+			tp->int_vector[i].status = 1;
+		}
+	}
+
+	return ret;
+}
+
+static int rtase_alloc_interrupt(struct pci_dev *pdev,
+				 struct rtase_private *tp)
+{
+	int ret;
+
+	ret = rtase_alloc_msix(pdev, tp);
+	if (ret != tp->int_nums) {
+		ret = pci_enable_msi(pdev);
+		if (ret)
+			dev_err(&pdev->dev,
+				"unable to alloc interrupt.(MSI)\n");
+		else
+			tp->sw_flag |= SWF_MSI_ENABLED;
+	} else {
+		tp->sw_flag |= SWF_MSIX_ENABLED;
+	}
+
+	return ret;
+}
+
+static void rtase_init_hardware(const struct rtase_private *tp)
+{
+	u16 i;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++)
+		rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
+}
+
+static void rtase_init_int_vector(struct rtase_private *tp)
+{
+	u16 i;
+
+	/* interrupt vector 0 */
+	tp->int_vector[0].tp = tp;
+	tp->int_vector[0].index = 0;
+	tp->int_vector[0].imr_addr = RTASE_IMR0;
+	tp->int_vector[0].isr_addr = RTASE_ISR0;
+	tp->int_vector[0].imr = ROK | RDU | TOK | TOK4 | TOK5 | TOK6 | TOK7;
+	tp->int_vector[0].poll = rtase_poll;
+
+	memset(tp->int_vector[0].name, 0x0, sizeof(tp->int_vector[0].name));
+	INIT_LIST_HEAD(&tp->int_vector[0].ring_list);
+
+	netif_napi_add(tp->dev, &tp->int_vector[0].napi,
+		       tp->int_vector[0].poll);
+	napi_enable(&tp->int_vector[0].napi);
+
+	/* interrupt vector 1 ~ 3 */
+	for (i = 1; i < tp->int_nums; i++) {
+		tp->int_vector[i].tp = tp;
+		tp->int_vector[i].index = i;
+		tp->int_vector[i].imr_addr = RTASE_IMR1 + (i - 1) * 4;
+		tp->int_vector[i].isr_addr = RTASE_ISR1 + (i - 1) * 4;
+		tp->int_vector[i].imr = Q_ROK | Q_RDU | Q_TOK;
+		tp->int_vector[i].poll = rtase_poll;
+
+		memset(tp->int_vector[i].name, 0x0, sizeof(tp->int_vector[0].name));
+		INIT_LIST_HEAD(&tp->int_vector[i].ring_list);
+
+		netif_napi_add(tp->dev, &tp->int_vector[i].napi,
+			       tp->int_vector[i].poll);
+		napi_enable(&tp->int_vector[i].napi);
+	}
+}
+
+static u16 rtase_calc_time_mitigation(u32 time_us)
+{
+	u16 int_miti;
+	u8 msb, time_count, time_unit;
+
+	time_us = min_t(int, time_us, MITI_MAX_TIME);
+
+	msb = fls(time_us);
+	if (msb >= MITI_COUNT_BIT_NUM) {
+		time_unit = msb - MITI_COUNT_BIT_NUM;
+		time_count = time_us >> (msb - MITI_COUNT_BIT_NUM);
+	} else {
+		time_unit = 0;
+		time_count = time_us;
+	}
+
+	int_miti = u16_encode_bits(time_count, MITI_TIME_COUNT_MASK) |
+		   u16_encode_bits(time_unit, MITI_TIME_UNIT_MASK);
+
+	return int_miti;
+}
+
+static u16 rtase_calc_packet_num_mitigation(u16 pkt_num)
+{
+	u16 int_miti;
+	u8 msb, pkt_num_count, pkt_num_unit;
+
+	pkt_num = min_t(int, pkt_num, MITI_MAX_PKT_NUM);
+
+	if (pkt_num > 60) {
+		pkt_num_unit = MITI_MAX_PKT_NUM_IDX;
+		pkt_num_count = pkt_num / MITI_MAX_PKT_NUM_UNIT;
+	} else {
+		msb = fls(pkt_num);
+		if (msb >= MITI_COUNT_BIT_NUM) {
+			pkt_num_unit = msb - MITI_COUNT_BIT_NUM;
+			pkt_num_count = pkt_num >> (msb - MITI_COUNT_BIT_NUM);
+		} else {
+			pkt_num_unit = 0;
+			pkt_num_count = pkt_num;
+		}
+	}
+
+	int_miti = u16_encode_bits(pkt_num_count, MITI_PKT_NUM_COUNT_MASK) |
+		   u16_encode_bits(pkt_num_unit, MITI_PKT_NUM_UNIT_MASK);
+
+	return int_miti;
+}
+
+static void rtase_init_software_variable(struct pci_dev *pdev,
+					 struct rtase_private *tp)
+{
+	u16 int_miti;
+
+	tp->tx_queue_ctrl = RTASE_TXQ_CTRL;
+	tp->func_tx_queue_num = RTASE_FUNC_TXQ_NUM;
+	tp->func_rx_queue_num = RTASE_FUNC_RXQ_NUM;
+	tp->int_nums = RTASE_INTERRUPT_NUM;
+
+	int_miti = rtase_calc_time_mitigation(MITI_DEFAULT_TIME) |
+		   rtase_calc_packet_num_mitigation(MITI_DEFAULT_PKT_NUM);
+	tp->tx_int_mit = int_miti;
+	tp->rx_int_mit = int_miti;
+
+	tp->sw_flag = 0;
+
+	rtase_init_int_vector(tp);
+
+	/* MTU range: 60 - hw-specific max */
+	tp->dev->min_mtu = ETH_ZLEN;
+	tp->dev->max_mtu = MAX_JUMBO_SIZE;
+}
+
+static bool rtase_check_mac_version_valid(struct rtase_private *tp)
+{
+	u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & HW_VER_MASK;
+	bool known_ver = false;
+
+	switch (hw_ver) {
+	case 0x00800000:
+	case 0x04000000:
+	case 0x04800000:
+		known_ver = true;
+		break;
+	}
+
+	return known_ver;
+}
+
+static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out,
+			    void __iomem **ioaddr_out)
+{
+	struct net_device *dev;
+	void __iomem *ioaddr;
+	int ret = -ENOMEM;
+
+	/* dev zeroed in alloc_etherdev */
+	dev = alloc_etherdev_mq(sizeof(struct rtase_private),
+				RTASE_FUNC_TXQ_NUM);
+	if (!dev)
+		goto err_out;
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_out_free_dev;
+
+	/* make sure PCI base addr 1 is MMIO */
+	if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+
+	/* check for weird/broken PCI region reporting */
+	if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+
+	ret = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (ret < 0)
+		goto err_out_disable;
+
+	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
+		dev->features |= NETIF_F_HIGHDMA;
+	else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+		goto err_out_free_res;
+	else
+		dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
+
+	pci_set_master(pdev);
+
+	/* ioremap MMIO region */
+	ioaddr = ioremap(pci_resource_start(pdev, 2),
+			 pci_resource_len(pdev, 2));
+	if (!ioaddr) {
+		ret = -EIO;
+		goto err_out_free_res;
+	}
+
+	*ioaddr_out = ioaddr;
+	*dev_out = dev;
+	goto out;
+
+err_out_free_res:
+	pci_release_regions(pdev);
+
+err_out_disable:
+	pci_disable_device(pdev);
+
+err_out_free_dev:
+	free_netdev(dev);
+
+err_out:
+	*ioaddr_out = NULL;
+	*dev_out = NULL;
+
+out:
+	return ret;
+}
+
+static void rtase_release_board(struct pci_dev *pdev, struct net_device *dev,
+				void __iomem *ioaddr)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_rar_set(tp, tp->org_mac_addr);
+	iounmap(ioaddr);
+
+	if ((tp->sw_flag & SWF_MSIX_ENABLED))
+		pci_disable_msix(pdev);
+	else
+		pci_disable_msi(pdev);
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	free_netdev(dev);
+}
+
+static int rtase_init_one(struct pci_dev *pdev,
+			  const struct pci_device_id *ent)
+{
+	struct net_device *dev = NULL;
+	void __iomem *ioaddr = NULL;
+	struct rtase_private *tp;
+	int ret;
+
+	if (!pdev->is_physfn && pdev->is_virtfn) {
+		dev_err(&pdev->dev, "This module does not support a virtual function.");
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "Automotive Switch Ethernet driver loaded\n");
+
+	ret = rtase_init_board(pdev, &dev, &ioaddr);
+	if (ret != 0)
+		return ret;
+
+	tp = netdev_priv(dev);
+	tp->mmio_addr = ioaddr;
+	tp->dev = dev;
+	tp->pdev = pdev;
+
+	/* identify chip attached to board */
+	if (!rtase_check_mac_version_valid(tp)) {
+		return dev_err_probe(&pdev->dev, -ENODEV,
+				     "unknown chip version, contact rtase maintainers (see MAINTAINERS file)\n");
+	}
+
+	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+	if (!dev->tstats)
+		goto err_out_1;
+
+	rtase_init_software_variable(pdev, tp);
+	rtase_init_hardware(tp);
+
+	ret = rtase_alloc_interrupt(pdev, tp);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to alloc MSIX/MSI\n");
+		goto err_out_1;
+	}
+
+	rtase_init_netdev_ops(dev);
+
+	dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+	dev->features |= NETIF_F_IP_CSUM;
+	dev->features |= NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+	dev->features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+	dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+			   NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX |
+			   NETIF_F_HW_VLAN_CTAG_RX;
+	dev->hw_features |= NETIF_F_RXALL;
+	dev->hw_features |= NETIF_F_RXFCS;
+	dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+	dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+			     NETIF_F_HIGHDMA;
+	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+	netif_set_tso_max_size(dev, LSO_64K);
+	netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
+
+	rtase_get_mac_address(dev);
+
+	tp->tally_vaddr = dma_alloc_coherent(&pdev->dev,
+					     sizeof(*tp->tally_vaddr),
+					     &tp->tally_paddr,
+					     GFP_KERNEL);
+	if (!tp->tally_vaddr) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	rtase_tally_counter_clear(tp);
+
+	pci_set_drvdata(pdev, dev);
+
+	ret = register_netdev(dev);
+	if (ret != 0)
+		goto err_out;
+
+	netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq);
+
+	netif_carrier_off(dev);
+
+	goto out;
+
+err_out:
+	if (tp->tally_vaddr) {
+		dma_free_coherent(&pdev->dev,
+				  sizeof(*tp->tally_vaddr),
+				  tp->tally_vaddr,
+				  tp->tally_paddr);
+
+		tp->tally_vaddr = NULL;
+	}
+
+err_out_1:
+	rtase_release_board(pdev, dev, ioaddr);
+
+out:
+	return ret;
+}
+
+static void rtase_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec;
+	u32 i;
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		netif_napi_del(&ivec->napi);
+	}
+
+	unregister_netdev(dev);
+	rtase_reset_interrupt(pdev, tp);
+	if (tp->tally_vaddr) {
+		dma_free_coherent(&pdev->dev,
+				  sizeof(*tp->tally_vaddr),
+				  tp->tally_vaddr,
+				  tp->tally_paddr);
+		tp->tally_vaddr = NULL;
+	}
+
+	rtase_release_board(pdev, dev, tp->mmio_addr);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static void rtase_shutdown(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	if (netif_running(dev))
+		rtase_close(dev);
+
+	rtase_reset_interrupt(pdev, tp);
+}
+
+static struct pci_driver rtase_pci_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = rtase_pci_tbl,
+	.probe = rtase_init_one,
+	.remove = rtase_remove_one,
+	.shutdown = rtase_shutdown,
+};
+
+module_pci_driver(rtase_pci_driver);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 02/13] net:ethernet:realtek:rtase: Implement the .ndo_open function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 01/13] net:ethernet:realtek:rtase: Add pci table supported in this module Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 03/13] net:ethernet:realtek:rtase: Implement the rtase_down function Justin Lai
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement the .ndo_open function to set default hardware settings
and initialize the descriptor ring and interrupts. Among them,
when requesting irq, because the first group of interrupts needs to
process more events, the overall structure will be different from
other groups of interrupts, so it needs to be processed separately.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 422 ++++++++++++++++++
 1 file changed, 422 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 6632a30da6ff..3dd8c6a9c7c1 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -130,6 +130,291 @@ static u32 rtase_r32(const struct rtase_private *tp, u16 reg)
 	return readl(tp->mmio_addr + reg);
 }
 
+static void rtase_set_rxbufsize(struct rtase_private *tp)
+{
+	tp->rx_buf_sz = RX_BUF_SIZE;
+}
+
+static int rtase_alloc_desc(struct rtase_private *tp)
+{
+	struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	/* rx and tx descriptors needs 256 bytes alignment.
+	 * dma_alloc_coherent provides more.
+	 */
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		tp->tx_ring[i].desc = dma_alloc_coherent(&pdev->dev,
+							 RTASE_TX_RING_DESC_SIZE,
+							 &tp->tx_ring[i].phy_addr,
+							 GFP_KERNEL);
+		if (!tp->tx_ring[i].desc)
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		tp->rx_ring[i].desc =
+			dma_alloc_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
+					   &tp->rx_ring[i].phy_addr,
+					   GFP_KERNEL);
+		if (!tp->rx_ring[i].desc)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void rtase_free_desc(struct rtase_private *tp)
+{
+	struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		if (!tp->tx_ring[i].desc)
+			continue;
+
+		dma_free_coherent(&pdev->dev, RTASE_TX_RING_DESC_SIZE,
+				  tp->tx_ring[i].desc,
+				  tp->tx_ring[i].phy_addr);
+		tp->tx_ring[i].desc = NULL;
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		if (!tp->rx_ring[i].desc)
+			continue;
+
+		dma_free_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
+				  tp->rx_ring[i].desc,
+				  tp->rx_ring[i].phy_addr);
+		tp->rx_ring[i].desc = NULL;
+	}
+}
+
+static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
+{
+	u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RING_END;
+
+	desc->desc_status.opts2 = 0;
+	/* force memory writes to complete before releasing descriptor */
+	dma_wmb();
+	WRITE_ONCE(desc->desc_cmd.opts1,
+		   cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
+}
+
+static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
+{
+	struct rtase_ring *ring = &tp->tx_ring[idx];
+	struct tx_desc *desc;
+	u32 i;
+
+	memset(ring->desc, 0x0, RTASE_TX_RING_DESC_SIZE);
+	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
+	ring->cur_idx = 0;
+	ring->dirty_idx = 0;
+	ring->index = idx;
+
+	for (i = 0; i < NUM_DESC; i++) {
+		ring->mis.len[i] = 0;
+		if ((NUM_DESC - 1) == i) {
+			desc = ring->desc + sizeof(struct tx_desc) * i;
+			desc->opts1 = cpu_to_le32(RING_END);
+		}
+	}
+
+	ring->ring_handler = tx_handler;
+	if (idx < 4) {
+		ring->ivec = &tp->int_vector[idx];
+		list_add_tail(&ring->ring_entry,
+			      &tp->int_vector[idx].ring_list);
+	} else {
+		ring->ivec = &tp->int_vector[0];
+		list_add_tail(&ring->ring_entry, &tp->int_vector[0].ring_list);
+	}
+}
+
+static void rtase_map_to_asic(union rx_desc *desc, dma_addr_t mapping,
+			      u32 rx_buf_sz)
+{
+	desc->desc_cmd.addr = cpu_to_le64(mapping);
+	/* make sure the physical address has been updated */
+	wmb();
+	rtase_mark_to_asic(desc, rx_buf_sz);
+}
+
+static void rtase_make_unusable_by_asic(union rx_desc *desc)
+{
+	desc->desc_cmd.addr = cpu_to_le64(RTK_MAGIC_NUMBER);
+	desc->desc_cmd.opts1 &= ~cpu_to_le32(DESC_OWN | RSVD_MASK);
+}
+
+static int rtase_alloc_rx_skb(const struct rtase_ring *ring,
+			      struct sk_buff **p_sk_buff, union rx_desc *desc,
+			      dma_addr_t *rx_phy_addr, u8 in_intr)
+{
+	struct rtase_int_vector *ivec = ring->ivec;
+	const struct rtase_private *tp = ivec->tp;
+	struct sk_buff *skb = NULL;
+	struct page *page;
+	dma_addr_t mapping;
+	void *buf_addr;
+	int ret = 0;
+
+	page = page_pool_dev_alloc_pages(tp->page_pool);
+	if (!page) {
+		netdev_err(tp->dev, "failed to alloc page\n");
+		goto err_out;
+	}
+
+	buf_addr = page_address(page);
+	mapping = page_pool_get_dma_addr(page);
+
+	skb = build_skb(buf_addr, PAGE_SIZE);
+	if (!skb) {
+		page_pool_put_full_page(tp->page_pool, page, true);
+		netdev_err(tp->dev, "failed to build skb\n");
+		goto err_out;
+	}
+
+	*p_sk_buff = skb;
+	*rx_phy_addr = mapping;
+	rtase_map_to_asic(desc, mapping, tp->rx_buf_sz);
+
+	return ret;
+
+err_out:
+	if (skb)
+		dev_kfree_skb(skb);
+
+	ret = -ENOMEM;
+	rtase_make_unusable_by_asic(desc);
+
+	return ret;
+}
+
+static u32 rtase_rx_ring_fill(struct rtase_ring *ring, u32 ring_start,
+			      u32 ring_end, u8 in_intr)
+{
+	union rx_desc *desc_base = ring->desc;
+	u32 cur;
+
+	for (cur = ring_start; ring_end - cur > 0; cur++) {
+		u32 i = cur % NUM_DESC;
+		union rx_desc *desc = desc_base + i;
+		int ret;
+
+		if (ring->skbuff[i])
+			continue;
+
+		ret = rtase_alloc_rx_skb(ring, &ring->skbuff[i], desc,
+					 &ring->mis.data_phy_addr[i],
+					 in_intr);
+		if (ret)
+			break;
+	}
+
+	return cur - ring_start;
+}
+
+static void rtase_mark_as_last_descriptor(union rx_desc *desc)
+{
+	desc->desc_cmd.opts1 |= cpu_to_le32(RING_END);
+}
+
+static void rtase_rx_ring_clear(struct rtase_ring *ring)
+{
+	union rx_desc *desc;
+	u32 i;
+
+	for (i = 0; i < NUM_DESC; i++) {
+		desc = ring->desc + sizeof(union rx_desc) * i;
+
+		if (!ring->skbuff[i])
+			continue;
+
+		dev_kfree_skb(ring->skbuff[i]);
+
+		ring->skbuff[i] = NULL;
+
+		rtase_make_unusable_by_asic(desc);
+	}
+}
+
+static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
+{
+	struct rtase_ring *ring = &tp->rx_ring[idx];
+	u16 i;
+
+	memset(ring->desc, 0x0, RTASE_RX_RING_DESC_SIZE);
+	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
+	ring->cur_idx = 0;
+	ring->dirty_idx = 0;
+	ring->index = idx;
+
+	for (i = 0; i < NUM_DESC; i++)
+		ring->mis.data_phy_addr[i] = 0;
+
+	ring->ring_handler = rx_handler;
+	ring->ivec = &tp->int_vector[idx];
+	list_add_tail(&ring->ring_entry, &tp->int_vector[idx].ring_list);
+}
+
+static void rtase_rx_clear(struct rtase_private *tp)
+{
+	u32 i;
+
+	for (i = 0; i < tp->func_rx_queue_num; i++)
+		rtase_rx_ring_clear(&tp->rx_ring[i]);
+
+	page_pool_destroy(tp->page_pool);
+	tp->page_pool = NULL;
+}
+
+static int rtase_init_ring(const struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct page_pool *page_pool;
+	struct page_pool_params pp_params = {
+		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+		.order = 0,
+		.pool_size = NUM_DESC * tp->func_rx_queue_num,
+		.nid = dev_to_node(&tp->pdev->dev),
+		.dev = &tp->pdev->dev,
+		.dma_dir = DMA_FROM_DEVICE,
+		.max_len = PAGE_SIZE,
+		.offset = 0,
+	};
+	u32 num;
+	u16 i;
+
+	page_pool = page_pool_create(&pp_params);
+	if (IS_ERR(page_pool)) {
+		netdev_err(tp->dev, "failed to create page pool\n");
+		return -ENOMEM;
+	}
+
+	tp->page_pool = page_pool;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++)
+		rtase_tx_desc_init(tp, i);
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		rtase_rx_desc_init(tp, i);
+		num = rtase_rx_ring_fill(&tp->rx_ring[i], 0, NUM_DESC, 0);
+		if (num != NUM_DESC)
+			goto err_out;
+
+		rtase_mark_as_last_descriptor(tp->rx_ring[i].desc +
+					      sizeof(union rx_desc) *
+					      (NUM_DESC - 1));
+	}
+
+	return 0;
+
+err_out:
+	rtase_rx_clear(tp);
+	return -ENOMEM;
+}
+
 static void rtase_tally_counter_clear(const struct rtase_private *tp)
 {
 	u32 cmd = lower_32_bits(tp->tally_paddr);
@@ -138,6 +423,133 @@ static void rtase_tally_counter_clear(const struct rtase_private *tp)
 	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
 }
 
+static void rtase_nic_enable(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rcr = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	u8 val;
+
+	/* PCIe PLA reload */
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr & ~PCIE_RELOAD_En);
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr | PCIE_RELOAD_En);
+
+	/* set PCIe TE & RE */
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val | TE | RE);
+
+	/* clear rxdv_gated_en */
+	val = rtase_r8(tp, RTASE_MISC);
+	rtase_w8(tp, RTASE_MISC, val & ~RX_DV_GATE_EN);
+}
+
+static void rtase_enable_hw_interrupt(const struct rtase_private *tp)
+{
+	const struct rtase_int_vector *ivec = &tp->int_vector[0];
+	u32 i;
+
+	rtase_w32(tp, ivec->imr_addr, ivec->imr);
+
+	for (i = 1; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		rtase_w16(tp, ivec->imr_addr, ivec->imr);
+	}
+}
+
+static void rtase_hw_start(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_nic_enable(dev);
+	rtase_enable_hw_interrupt(tp);
+}
+
+static int rtase_open(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec = &tp->int_vector[0];
+	const struct pci_dev *pdev = tp->pdev;
+	int ret;
+	u16 i;
+
+	rtase_set_rxbufsize(tp);
+
+	ret = rtase_alloc_desc(tp);
+	if (ret)
+		goto err_free_all_allocated_mem;
+
+	ret = rtase_init_ring(dev);
+	if (ret)
+		goto err_free_all_allocated_mem;
+
+	INIT_DELAYED_WORK(&tp->task, NULL);
+
+	rtase_hw_config(dev);
+
+	if (tp->sw_flag & SWF_MSIX_ENABLED) {
+		ret = request_irq(ivec->irq, rtase_interrupt, 0,
+				  dev->name, ivec);
+
+		/* request other interrupts to handle multiqueue */
+		for (i = 1; i < tp->int_nums; i++) {
+			if (ret)
+				continue;
+
+			ivec = &tp->int_vector[i];
+			if (ivec->status != 1)
+				continue;
+
+			snprintf(ivec->name, sizeof(ivec->name), "%s_int%i", tp->dev->name, i);
+			ret = request_irq(ivec->irq, rtase_q_interrupt, 0,
+					  ivec->name, ivec);
+		}
+	} else if (tp->sw_flag & SWF_MSI_ENABLED) {
+		ret = request_irq(pdev->irq, rtase_interrupt, 0, dev->name,
+				  ivec);
+	} else {
+		ret = request_irq(pdev->irq, rtase_interrupt, IRQF_SHARED,
+				  dev->name, ivec);
+	}
+
+	if (ret != 0) {
+		netdev_err(dev, "can't request MSIX interrupt. Error: %d\n", ret);
+		goto err_free_all_allocated_mem;
+	}
+
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+
+	goto out;
+
+err_free_all_allocated_mem:
+	rtase_free_desc(tp);
+
+out:
+	return ret;
+}
+
+static int rtase_close(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	const struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	rtase_down(dev);
+
+	if (tp->sw_flag & SWF_MSIX_ENABLED) {
+		for (i = 0; i < tp->int_nums; i++)
+			free_irq(tp->int_vector[i].irq, &tp->int_vector[i]);
+
+	} else {
+		free_irq(pdev->irq, &tp->int_vector[0]);
+	}
+
+	rtase_free_desc(tp);
+
+	return 0;
+}
+
 static void rtase_enable_eem_write(const struct rtase_private *tp)
 {
 	u8 val;
@@ -170,6 +582,11 @@ static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
 	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
 }
 
+static const struct net_device_ops rtase_netdev_ops = {
+	.ndo_open = rtase_open,
+	.ndo_stop = rtase_close,
+};
+
 static void rtase_get_mac_address(struct net_device *dev)
 {
 	struct rtase_private *tp = netdev_priv(dev);
@@ -193,6 +610,11 @@ static void rtase_get_mac_address(struct net_device *dev)
 	ether_addr_copy(dev->perm_addr, dev->dev_addr);
 }
 
+static void rtase_init_netdev_ops(struct net_device *dev)
+{
+	dev->netdev_ops = &rtase_netdev_ops;
+}
+
 static void rtase_reset_interrupt(struct pci_dev *pdev,
 				  const struct rtase_private *tp)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 03/13] net:ethernet:realtek:rtase: Implement the rtase_down function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 01/13] net:ethernet:realtek:rtase: Add pci table supported in this module Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 02/13] net:ethernet:realtek:rtase: Implement the .ndo_open function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 04/13] net:ethernet:realtek:rtase: Implement the interrupt routine and rtase_poll Justin Lai
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement the rtase_down function to disable hardware setting
and interrupt and clear descriptor ring.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 3dd8c6a9c7c1..17e1f610c89f 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -190,6 +190,57 @@ static void rtase_free_desc(struct rtase_private *tp)
 	}
 }
 
+static void rtase_unmap_tx_skb(struct pci_dev *pdev, u32 len,
+			       struct tx_desc *desc)
+{
+	dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len,
+			 DMA_TO_DEVICE);
+	desc->opts1 = cpu_to_le32(RTK_OPTS1_DEBUG_VALUE);
+	desc->opts2 = 0x00;
+	desc->addr = cpu_to_le64(RTK_MAGIC_NUMBER);
+}
+
+static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	struct net_device *dev = tp->dev;
+	struct tx_desc *desc_base = ring->desc;
+	u32 i;
+
+	for (i = 0; i < n; i++) {
+		u32 entry = (start + i) % NUM_DESC;
+		struct tx_desc *desc = desc_base + entry;
+		u32 len = ring->mis.len[entry];
+		struct sk_buff *skb;
+
+		if (len == 0)
+			continue;
+
+		rtase_unmap_tx_skb(tp->pdev, len, desc);
+		ring->mis.len[entry] = 0;
+		skb = ring->skbuff[entry];
+		if (!skb)
+			continue;
+
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_any(skb);
+		ring->skbuff[entry] = NULL;
+	}
+}
+
+static void rtase_tx_clear(struct rtase_private *tp)
+{
+	struct rtase_ring *ring;
+	u16 i;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		ring = &tp->tx_ring[i];
+		rtase_tx_clear_range(ring, ring->dirty_idx, NUM_DESC);
+		ring->cur_idx = 0;
+		ring->dirty_idx = 0;
+	}
+}
+
 static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
 {
 	u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RING_END;
@@ -423,6 +474,77 @@ static void rtase_tally_counter_clear(const struct rtase_private *tp)
 	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
 }
 
+static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
+{
+	const struct rtase_int_vector *ivec = &tp->int_vector[0];
+	u32 val1;
+	u16 val2;
+	u8 i;
+
+	rtase_w32(tp, ivec->imr_addr, 0);
+	val1 = rtase_r32(tp, ivec->isr_addr);
+	rtase_w32(tp, ivec->isr_addr, val1);
+
+	for (i = 1; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		rtase_w16(tp, ivec->imr_addr, 0);
+		val2 = rtase_r16(tp, ivec->isr_addr);
+		rtase_w16(tp, ivec->isr_addr, val2);
+	}
+}
+
+static void rtase_poll_timeout(const struct rtase_private *tp, u32 cond,
+			       u32 sleep_us, u64 timeout_us, u16 reg)
+{
+	int err;
+	u8 val;
+
+	err = read_poll_timeout(rtase_r8, val, val & cond, sleep_us, timeout_us,
+				false, tp, reg);
+
+	if (err == -ETIMEDOUT)
+		netdev_err(tp->dev, "poll reg 0x00%x timeout\n", reg);
+}
+
+static void rtase_nic_reset(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rx_config;
+	u8 val;
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config & ~ACCEPT_MASK);
+
+	val = rtase_r8(tp, RTASE_MISC);
+	rtase_w8(tp, RTASE_MISC, val | RX_DV_GATE_EN);
+
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val | STOP_REQ);
+	mdelay(2);
+
+	rtase_poll_timeout(tp, STOP_REQ_DONE, 100, 150000, RTASE_CHIP_CMD);
+
+	rtase_poll_timeout(tp, TX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
+
+	rtase_poll_timeout(tp, RX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
+
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val & ~(TE | RE));
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val & ~STOP_REQ);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
+}
+
+static void rtase_hw_reset(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_irq_dis_and_clear(tp);
+
+	rtase_nic_reset(dev);
+}
+
 static void rtase_nic_enable(const struct net_device *dev)
 {
 	const struct rtase_private *tp = netdev_priv(dev);
@@ -529,6 +651,28 @@ static int rtase_open(struct net_device *dev)
 	return ret;
 }
 
+static void rtase_down(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 i;
+
+	netif_stop_queue(dev);
+
+	/* give a racing hard_start_xmit a few cycles to complete */
+	synchronize_rcu();
+
+	netif_carrier_off(dev);
+
+	rtase_hw_reset(dev);
+
+	for (i = 0; i < tp->int_nums; i++)
+		synchronize_irq(tp->int_vector[i].irq);
+
+	rtase_tx_clear(tp);
+
+	rtase_rx_clear(tp);
+}
+
 static int rtase_close(struct net_device *dev)
 {
 	struct rtase_private *tp = netdev_priv(dev);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 04/13] net:ethernet:realtek:rtase: Implement the interrupt routine and rtase_poll
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (2 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 03/13] net:ethernet:realtek:rtase: Implement the rtase_down function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

1. Implement rtase_interrupt to handle txQ0/rxQ0, txQ4~txQ7 interrupts,
and implement rtase_q_interrupt to handle txQ1/rxQ1, txQ2/rxQ2 and
txQ3/rxQ3 interrupts.
2. Implement rtase_poll to call ring_handler to process the tx or
rx packet of each ring. If the returned value is budget,it means that
there is still work of a certain ring that has not yet been completed.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 89 +++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 17e1f610c89f..46d128a68844 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -585,6 +585,76 @@ static void rtase_hw_start(const struct net_device *dev)
 	rtase_enable_hw_interrupt(tp);
 }
 
+/*  the interrupt handler does RXQ0 and TXQ0, TXQ4~7 interrutp status
+ */
+static irqreturn_t rtase_interrupt(int irq, void *dev_instance)
+{
+	const struct rtase_private *tp;
+	struct rtase_int_vector *ivec;
+	u32 status;
+
+	ivec = dev_instance;
+	tp = ivec->tp;
+	status = rtase_r32(tp, ivec->isr_addr);
+
+	rtase_w32(tp, ivec->imr_addr, 0x0);
+	rtase_w32(tp, ivec->isr_addr, status & ~FOVW);
+
+	if (napi_schedule_prep(&ivec->napi))
+		__napi_schedule(&ivec->napi);
+
+	return IRQ_HANDLED;
+}
+
+/*  the interrupt handler does RXQ1&TXQ1 or RXQ2&TXQ2 or RXQ3&TXQ3 interrupt
+ *  status according to interrupt vector
+ */
+static irqreturn_t rtase_q_interrupt(int irq, void *dev_instance)
+{
+	const struct rtase_private *tp;
+	struct rtase_int_vector *ivec;
+	u16 status;
+
+	ivec = dev_instance;
+	tp = ivec->tp;
+	status = rtase_r16(tp, ivec->isr_addr);
+
+	rtase_w16(tp, ivec->imr_addr, 0x0);
+	rtase_w16(tp, ivec->isr_addr, status);
+
+	if (napi_schedule_prep(&ivec->napi))
+		__napi_schedule(&ivec->napi);
+
+	return IRQ_HANDLED;
+}
+
+static int rtase_poll(struct napi_struct *napi, int budget)
+{
+	const struct rtase_int_vector *ivec;
+	const struct rtase_private *tp;
+	struct rtase_ring *ring;
+	int total_workdone = 0;
+
+	ivec = container_of(napi, struct rtase_int_vector, napi);
+	tp = ivec->tp;
+
+	list_for_each_entry(ring, &ivec->ring_list, ring_entry) {
+		total_workdone += ring->ring_handler(ring, budget);
+	}
+
+	if (total_workdone >= budget)
+		return budget;
+
+	if (napi_complete_done(napi, total_workdone)) {
+		if (!ivec->index)
+			rtase_w32(tp, ivec->imr_addr, ivec->imr);
+		else
+			rtase_w16(tp, ivec->imr_addr, ivec->imr);
+	}
+
+	return total_workdone;
+}
+
 static int rtase_open(struct net_device *dev)
 {
 	struct rtase_private *tp = netdev_priv(dev);
@@ -726,9 +796,28 @@ static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
 	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/* Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void rtase_netpoll(struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct pci_dev *pdev = tp->pdev;
+
+	disable_irq(pdev->irq);
+	rtase_interrupt(pdev->irq, dev);
+	enable_irq(pdev->irq);
+}
+#endif
+
 static const struct net_device_ops rtase_netdev_ops = {
 	.ndo_open = rtase_open,
 	.ndo_stop = rtase_close,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = rtase_netpoll,
+#endif
 };
 
 static void rtase_get_mac_address(struct net_device *dev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (3 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 04/13] net:ethernet:realtek:rtase: Implement the interrupt routine and rtase_poll Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 15:02   ` Heiner Kallweit
                     ` (2 more replies)
  2023-11-15 13:34 ` [PATCH net-next v11 06/13] net:ethernet:realtek:rtase: Implement .ndo_start_xmit function Justin Lai
                   ` (8 subsequent siblings)
  13 siblings, 3 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement rtase_hw_config to set default hardware settings, including
setting interrupt mitigation, tx/rx DMA burst, interframe gap time,
rx packet filter, near fifo threshold and fill descriptor ring and
tally counter address, and enable flow control. When filling the
rx descriptor ring, the first group of queues needs to be processed
separately because the positions of the first group of queues are not
regular with other subsequent groups. The other queues are all newly
added features, but we want to retain the original design. So they were
not put together.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   |  243 ++
 drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
 drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++
 3 files changed, 3138 insertions(+)
 create mode 100644 drivers/net/ethernet/realtek/rtase/tt.c
 create mode 100644 drivers/net/ethernet/realtek/rtase/tt.h

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 46d128a68844..4e04189050cc 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -466,6 +466,25 @@ static int rtase_init_ring(const struct net_device *dev)
 	return -ENOMEM;
 }
 
+static void rtase_interrupt_mitigation(const struct rtase_private *tp)
+{
+	u32 i;
+
+	/* tx interrupt mitigation */
+	for (i = 0; i < tp->func_tx_queue_num; i++)
+		rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
+
+	/* rx interrupt mitigation */
+	for (i = 0; i < tp->func_rx_queue_num; i++)
+		rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
+}
+
+static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
+{
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
+	rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
+}
+
 static void rtase_tally_counter_clear(const struct rtase_private *tp)
 {
 	u32 cmd = lower_32_bits(tp->tally_paddr);
@@ -474,6 +493,123 @@ static void rtase_tally_counter_clear(const struct rtase_private *tp)
 	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
 }
 
+static void rtase_desc_addr_fill(const struct rtase_private *tp)
+{
+	const struct rtase_ring *ring;
+	u16 i, cmd, val;
+	int err;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		ring = &tp->tx_ring[i];
+
+		rtase_w32(tp, RTASE_TX_DESC_ADDR0,
+			  lower_32_bits(ring->phy_addr));
+		rtase_w32(tp, RTASE_TX_DESC_ADDR4,
+			  upper_32_bits(ring->phy_addr));
+
+		cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
+		rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
+
+		err = read_poll_timeout(rtase_r16, val, !(val & TX_DESC_CMD_CS),
+					10, 1000, false, tp, RTASE_TX_DESC_COMMAND);
+
+		if (err == -ETIMEDOUT)
+			netdev_err(tp->dev, "error occurred in fill tx descriptor\n");
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		ring = &tp->rx_ring[i];
+
+		if (i == 0) {
+			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
+				  lower_32_bits(ring->phy_addr));
+			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
+				  upper_32_bits(ring->phy_addr));
+		} else {
+			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i - 1) * 8)),
+				  lower_32_bits(ring->phy_addr));
+			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i - 1) * 8)),
+				  upper_32_bits(ring->phy_addr));
+		}
+	}
+}
+
+static void rtase_hw_set_features(const struct net_device *dev,
+				  netdev_features_t features)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rx_config, val;
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	if (features & NETIF_F_RXALL)
+		rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
+	else
+		rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
+
+	val = rtase_r16(tp, RTASE_CPLUS_CMD);
+	if (features & NETIF_F_RXCSUM)
+		rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
+	else
+		rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
+		rx_config |= (INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
+	else
+		rx_config &= ~(INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
+}
+
+static void rtase_set_mar(const struct rtase_private *tp)
+{
+	rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
+	rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
+}
+
+static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
+	u16 rx_mode;
+
+	rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
+	rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
+
+	if (dev->flags & IFF_PROMISC) {
+		rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
+	} else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
+		   (dev->flags & IFF_ALLMULTI)) {
+		/* too many to filter perfectly -- accept all multicasts */
+		rx_mode |= ACCEPT_MULTICAST;
+	} else {
+		struct netdev_hw_addr *hw_addr;
+
+		mc_filter[0] = 0;
+		mc_filter[1] = 0;
+
+		netdev_for_each_mc_addr(hw_addr, dev) {
+			u32 bit_nr = eth_hw_addr_crc(hw_addr);
+			u32 idx = u32_get_bits(bit_nr, BIT(31));
+			u32 bit = u32_get_bits(bit_nr, MULTICAST_FILTER_MASK);
+
+			mc_filter[idx] |= BIT(bit);
+			rx_mode |= ACCEPT_MULTICAST;
+		}
+	}
+
+	if (dev->features & NETIF_F_RXALL)
+		rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
+
+	tp->mc_filter[0] = swab32(mc_filter[1]);
+	tp->mc_filter[1] = swab32(mc_filter[0]);
+
+	rtase_set_mar(tp);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
+}
+
 static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
 {
 	const struct rtase_int_vector *ivec = &tp->int_vector[0];
@@ -545,6 +681,113 @@ static void rtase_hw_reset(const struct net_device *dev)
 	rtase_nic_reset(dev);
 }
 
+static void rtase_set_rx_queue(const struct rtase_private *tp)
+{
+	u16 reg_data;
+
+	reg_data = rtase_r16(tp, RTASE_FCR);
+	switch (tp->func_rx_queue_num) {
+	case 1:
+		u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
+		break;
+	case 2:
+		u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
+		break;
+	case 4:
+		u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
+		break;
+	}
+	rtase_w16(tp, RTASE_FCR, reg_data);
+}
+
+static void rtase_set_tx_queue(const struct rtase_private *tp)
+{
+	u16 reg_data;
+
+	reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
+	switch (tp->tx_queue_ctrl) {
+	case 1:
+		u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
+		break;
+	case 2:
+		u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
+		break;
+	case 3:
+	case 4:
+		u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
+		break;
+	default:
+		u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
+		break;
+	}
+	rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
+}
+
+static void rtase_hw_config(struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u32 reg_data32;
+	u16 reg_data16;
+
+	rtase_hw_reset(dev);
+
+	/* Set Rx DMA burst */
+	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
+	u16p_replace_bits(&reg_data16, RX_DMA_BURST_256, RX_MX_DMA_MASK);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
+
+	/* New Rx Descritpor */
+	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
+	u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
+	rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
+
+	rtase_set_rx_queue(tp);
+
+	/* interrupt mitigation */
+	rtase_interrupt_mitigation(tp);
+
+	/* set tx DMA burst size and interframe gap time */
+	reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
+	u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED, TX_DMA_MASK);
+	u32p_replace_bits(&reg_data32, INTERFRAMEGAP, TX_INTER_FRAME_GAP_MASK);
+	rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
+
+	/* new tx Descriptor */
+	reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
+	rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 | TX_NEW_DESC_FORMAT_EN);
+
+	/* tx Fetch Desc Number */
+	rtase_w8(tp, RTASE_TDFNR, 0x10);
+
+	/* tag num select */
+	reg_data16 = rtase_r16(tp, RTASE_MTPS);
+	u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
+	rtase_w16(tp, RTASE_MTPS, reg_data16);
+
+	rtase_set_tx_queue(tp);
+
+	/* TOK condition */
+	rtase_w16(tp, RTASE_TOKSEL, 0x5555);
+
+	rtase_tally_counter_addr_fill(tp);
+	rtase_desc_addr_fill(tp);
+	rtase_hw_set_features(dev, dev->features);
+
+	/* enable flow control */
+	reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
+	reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
+	rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
+	/* set Near FIFO Threshold - rx missed issue. */
+	rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
+
+	rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
+
+	/* set Rx packet filter */
+	rtase_hw_set_rx_packet_filter(dev);
+}
+
 static void rtase_nic_enable(const struct net_device *dev)
 {
 	const struct rtase_private *tp = netdev_priv(dev);
diff --git a/drivers/net/ethernet/realtek/rtase/tt.c b/drivers/net/ethernet/realtek/rtase/tt.c
new file mode 100644
index 000000000000..5ea4d51fcc47
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/tt.c
@@ -0,0 +1,2542 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ *  rtase is the Linux device driver released for Realtek Automotive Switch
+ *  controllers with PCI-Express interface.
+ *
+ *  Copyright(c) 2023 Realtek Semiconductor Corp.
+ *
+ *  Below is a simplified block diagram of the chip and its relevant interfaces.
+ *
+ *               *************************
+ *               *                       *
+ *               *  CPU network device   *
+ *               *                       *
+ *               *   +-------------+     *
+ *               *   |  PCIE Host  |     *
+ *               ***********++************
+ *                          ||
+ *                         PCIE
+ *                          ||
+ *      ********************++**********************
+ *      *            | PCIE Endpoint |             *
+ *      *            +---------------+             *
+ *      *                | GMAC |                  *
+ *      *                +--++--+  Realtek         *
+ *      *                   ||     RTL90xx Series  *
+ *      *                   ||                     *
+ *      *     +-------------++----------------+    *
+ *      *     |           | MAC |             |    *
+ *      *     |           +-----+             |    *
+ *      *     |                               |    *
+ *      *     |     Ethernet Switch Core      |    *
+ *      *     |                               |    *
+ *      *     |   +-----+           +-----+   |    *
+ *      *     |   | MAC |...........| MAC |   |    *
+ *      *     +---+-----+-----------+-----+---+    *
+ *      *         | PHY |...........| PHY |        *
+ *      *         +--++-+           +--++-+        *
+ *      *************||****************||***********
+ *
+ *  The block of the Realtek RTL90xx series is our entire chip architecture,
+ *  the GMAC is connected to the switch core, and there is no PHY in between.
+ *  In addition, this driver is mainly used to control GMAC, but does not
+ *  control the switch core, so it is not the same as DSA.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/prefetch.h>
+#include <linux/rtnetlink.h>
+#include <linux/tcp.h>
+#include <asm/irq.h>
+#include <net/ip6_checksum.h>
+#include <net/page_pool/helpers.h>
+#include <net/pkt_cls.h>
+
+#include "rtase.h"
+
+#define RTK_OPTS1_DEBUG_VALUE 0x0BADBEEF
+#define RTK_MAGIC_NUMBER      0x0BADBADBADBADBAD
+
+static const struct pci_device_id rtase_pci_tbl[] = {
+	{PCI_VDEVICE(REALTEK, 0x906A)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, rtase_pci_tbl);
+
+MODULE_AUTHOR("Realtek ARD Software Team");
+MODULE_DESCRIPTION("Network Driver for the PCIe interface of Realtek Automotive Ethernet Switch");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct rtase_counters {
+	__le64 tx_packets;
+	__le64 rx_packets;
+	__le64 tx_errors;
+	__le32 rx_errors;
+	__le16 rx_missed;
+	__le16 align_errors;
+	__le32 tx_one_collision;
+	__le32 tx_multi_collision;
+	__le64 rx_unicast;
+	__le64 rx_broadcast;
+	__le32 rx_multicast;
+	__le16 tx_aborted;
+	__le16 tx_underun;
+} __packed;
+
+static void rtase_w8(const struct rtase_private *tp, u16 reg, u8 val8)
+{
+	writeb(val8, tp->mmio_addr + reg);
+}
+
+static void rtase_w16(const struct rtase_private *tp, u16 reg, u16 val16)
+{
+	writew(val16, tp->mmio_addr + reg);
+}
+
+static void rtase_w32(const struct rtase_private *tp, u16 reg, u32 val32)
+{
+	writel(val32, tp->mmio_addr + reg);
+}
+
+static u8 rtase_r8(const struct rtase_private *tp, u16 reg)
+{
+	return readb(tp->mmio_addr + reg);
+}
+
+static u16 rtase_r16(const struct rtase_private *tp, u16 reg)
+{
+	return readw(tp->mmio_addr + reg);
+}
+
+static u32 rtase_r32(const struct rtase_private *tp, u16 reg)
+{
+	return readl(tp->mmio_addr + reg);
+}
+
+static void rtase_set_rxbufsize(struct rtase_private *tp)
+{
+	tp->rx_buf_sz = RX_BUF_SIZE;
+}
+
+static int rtase_alloc_desc(struct rtase_private *tp)
+{
+	struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	/* rx and tx descriptors needs 256 bytes alignment.
+	 * dma_alloc_coherent provides more.
+	 */
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		tp->tx_ring[i].desc = dma_alloc_coherent(&pdev->dev,
+							 RTASE_TX_RING_DESC_SIZE,
+							 &tp->tx_ring[i].phy_addr,
+							 GFP_KERNEL);
+		if (!tp->tx_ring[i].desc)
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		tp->rx_ring[i].desc =
+			dma_alloc_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
+					   &tp->rx_ring[i].phy_addr,
+					   GFP_KERNEL);
+		if (!tp->rx_ring[i].desc)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void rtase_free_desc(struct rtase_private *tp)
+{
+	struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		if (!tp->tx_ring[i].desc)
+			continue;
+
+		dma_free_coherent(&pdev->dev, RTASE_TX_RING_DESC_SIZE,
+				  tp->tx_ring[i].desc,
+				  tp->tx_ring[i].phy_addr);
+		tp->tx_ring[i].desc = NULL;
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		if (!tp->rx_ring[i].desc)
+			continue;
+
+		dma_free_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
+				  tp->rx_ring[i].desc,
+				  tp->rx_ring[i].phy_addr);
+		tp->rx_ring[i].desc = NULL;
+	}
+}
+
+static void rtase_unmap_tx_skb(struct pci_dev *pdev, u32 len,
+			       struct tx_desc *desc)
+{
+	dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len,
+			 DMA_TO_DEVICE);
+	desc->opts1 = cpu_to_le32(RTK_OPTS1_DEBUG_VALUE);
+	desc->opts2 = 0x00;
+	desc->addr = cpu_to_le64(RTK_MAGIC_NUMBER);
+}
+
+static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	struct net_device *dev = tp->dev;
+	struct tx_desc *desc_base = ring->desc;
+	u32 i;
+
+	for (i = 0; i < n; i++) {
+		u32 entry = (start + i) % NUM_DESC;
+		struct tx_desc *desc = desc_base + entry;
+		u32 len = ring->mis.len[entry];
+		struct sk_buff *skb;
+
+		if (len == 0)
+			continue;
+
+		rtase_unmap_tx_skb(tp->pdev, len, desc);
+		ring->mis.len[entry] = 0;
+		skb = ring->skbuff[entry];
+		if (!skb)
+			continue;
+
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_any(skb);
+		ring->skbuff[entry] = NULL;
+	}
+}
+
+static void rtase_tx_clear(struct rtase_private *tp)
+{
+	struct rtase_ring *ring;
+	u16 i;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		ring = &tp->tx_ring[i];
+		rtase_tx_clear_range(ring, ring->dirty_idx, NUM_DESC);
+		ring->cur_idx = 0;
+		ring->dirty_idx = 0;
+	}
+}
+
+static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
+{
+	u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RING_END;
+
+	desc->desc_status.opts2 = 0;
+	/* force memory writes to complete before releasing descriptor */
+	dma_wmb();
+	WRITE_ONCE(desc->desc_cmd.opts1,
+		   cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
+}
+
+static bool rtase_tx_avail(struct rtase_ring *ring)
+{
+	u32 avail_num = READ_ONCE(ring->dirty_idx) + NUM_DESC -
+			READ_ONCE(ring->cur_idx);
+
+	return avail_num > MAX_SKB_FRAGS;
+}
+
+static int tx_handler(struct rtase_ring *ring, int budget)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	struct net_device *dev = tp->dev;
+	int workdone = 0;
+	u32 dirty_tx;
+	u32 tx_left;
+
+	dirty_tx = ring->dirty_idx;
+	tx_left = READ_ONCE(ring->cur_idx) - dirty_tx;
+
+	while (tx_left > 0) {
+		u32 entry = dirty_tx % NUM_DESC;
+		struct tx_desc *desc = ring->desc +
+				       sizeof(struct tx_desc) * entry;
+		u32 len = ring->mis.len[entry];
+		u32 status;
+
+		status = le32_to_cpu(desc->opts1);
+
+		if (status & DESC_OWN)
+			break;
+
+		rtase_unmap_tx_skb(tp->pdev, len, desc);
+		ring->mis.len[entry] = 0;
+		if (ring->skbuff[entry]) {
+			dev_consume_skb_any(ring->skbuff[entry]);
+			ring->skbuff[entry] = NULL;
+		}
+
+		dev->stats.tx_bytes += len;
+		dev->stats.tx_packets++;
+		dirty_tx++;
+		tx_left--;
+		workdone++;
+
+		if (workdone == budget)
+			break;
+	}
+
+	if (ring->dirty_idx != dirty_tx) {
+		WRITE_ONCE(ring->dirty_idx, dirty_tx);
+
+		if (__netif_subqueue_stopped(dev, ring->index) &&
+		    rtase_tx_avail(ring))
+			netif_start_subqueue(dev, ring->index);
+
+		if (ring->cur_idx != dirty_tx)
+			rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+	}
+
+	return workdone;
+}
+
+static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
+{
+	struct rtase_ring *ring = &tp->tx_ring[idx];
+	struct tx_desc *desc;
+	u32 i;
+
+	memset(ring->desc, 0x0, RTASE_TX_RING_DESC_SIZE);
+	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
+	ring->cur_idx = 0;
+	ring->dirty_idx = 0;
+	ring->index = idx;
+
+	for (i = 0; i < NUM_DESC; i++) {
+		ring->mis.len[i] = 0;
+		if ((NUM_DESC - 1) == i) {
+			desc = ring->desc + sizeof(struct tx_desc) * i;
+			desc->opts1 = cpu_to_le32(RING_END);
+		}
+	}
+
+	ring->ring_handler = tx_handler;
+	if (idx < 4) {
+		ring->ivec = &tp->int_vector[idx];
+		list_add_tail(&ring->ring_entry,
+			      &tp->int_vector[idx].ring_list);
+	} else {
+		ring->ivec = &tp->int_vector[0];
+		list_add_tail(&ring->ring_entry, &tp->int_vector[0].ring_list);
+	}
+}
+
+static void rtase_map_to_asic(union rx_desc *desc, dma_addr_t mapping,
+			      u32 rx_buf_sz)
+{
+	desc->desc_cmd.addr = cpu_to_le64(mapping);
+	/* make sure the physical address has been updated */
+	wmb();
+	rtase_mark_to_asic(desc, rx_buf_sz);
+}
+
+static void rtase_make_unusable_by_asic(union rx_desc *desc)
+{
+	desc->desc_cmd.addr = cpu_to_le64(RTK_MAGIC_NUMBER);
+	desc->desc_cmd.opts1 &= ~cpu_to_le32(DESC_OWN | RSVD_MASK);
+}
+
+static int rtase_alloc_rx_skb(const struct rtase_ring *ring,
+			      struct sk_buff **p_sk_buff, union rx_desc *desc,
+			      dma_addr_t *rx_phy_addr, u8 in_intr)
+{
+	struct rtase_int_vector *ivec = ring->ivec;
+	const struct rtase_private *tp = ivec->tp;
+	struct sk_buff *skb = NULL;
+	struct page *page;
+	dma_addr_t mapping;
+	void *buf_addr;
+	int ret = 0;
+
+	page = page_pool_dev_alloc_pages(tp->page_pool);
+	if (!page) {
+		netdev_err(tp->dev, "failed to alloc page\n");
+		goto err_out;
+	}
+
+	buf_addr = page_address(page);
+	mapping = page_pool_get_dma_addr(page);
+
+	skb = build_skb(buf_addr, PAGE_SIZE);
+	if (!skb) {
+		page_pool_put_full_page(tp->page_pool, page, true);
+		netdev_err(tp->dev, "failed to build skb\n");
+		goto err_out;
+	}
+
+	*p_sk_buff = skb;
+	*rx_phy_addr = mapping;
+	rtase_map_to_asic(desc, mapping, tp->rx_buf_sz);
+
+	return ret;
+
+err_out:
+	if (skb)
+		dev_kfree_skb(skb);
+
+	ret = -ENOMEM;
+	rtase_make_unusable_by_asic(desc);
+
+	return ret;
+}
+
+static u32 rtase_rx_ring_fill(struct rtase_ring *ring, u32 ring_start,
+			      u32 ring_end, u8 in_intr)
+{
+	union rx_desc *desc_base = ring->desc;
+	u32 cur;
+
+	for (cur = ring_start; ring_end - cur > 0; cur++) {
+		u32 i = cur % NUM_DESC;
+		union rx_desc *desc = desc_base + i;
+		int ret;
+
+		if (ring->skbuff[i])
+			continue;
+
+		ret = rtase_alloc_rx_skb(ring, &ring->skbuff[i], desc,
+					 &ring->mis.data_phy_addr[i],
+					 in_intr);
+		if (ret)
+			break;
+	}
+
+	return cur - ring_start;
+}
+
+static void rtase_mark_as_last_descriptor(union rx_desc *desc)
+{
+	desc->desc_cmd.opts1 |= cpu_to_le32(RING_END);
+}
+
+static void rtase_rx_ring_clear(struct rtase_ring *ring)
+{
+	union rx_desc *desc;
+	u32 i;
+
+	for (i = 0; i < NUM_DESC; i++) {
+		desc = ring->desc + sizeof(union rx_desc) * i;
+
+		if (!ring->skbuff[i])
+			continue;
+
+		dev_kfree_skb(ring->skbuff[i]);
+
+		ring->skbuff[i] = NULL;
+
+		rtase_make_unusable_by_asic(desc);
+	}
+}
+
+static int rtase_fragmented_frame(u32 status)
+{
+	return (status & (RX_FIRST_FRAG | RX_LAST_FRAG)) !=
+		(RX_FIRST_FRAG | RX_LAST_FRAG);
+}
+
+static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff *skb,
+			  const union rx_desc *desc)
+{
+	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+	/* rx csum offload */
+	if (((opts2 & RX_V4F) && !(opts2 & RX_IPF)) || (opts2 & RX_V6F)) {
+		if (((opts2 & RX_TCPT) && !(opts2 & RX_TCPF)) ||
+		    ((opts2 & RX_UDPT) && !(opts2 & RX_UDPF))) {
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		} else {
+			skb->ip_summed = CHECKSUM_NONE;
+		}
+	} else {
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+}
+
+static void rtase_rx_vlan_skb(union rx_desc *desc, struct sk_buff *skb)
+{
+	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+	if (!(opts2 & RX_VLAN_TAG))
+		return;
+
+	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & VLAN_TAG_MASK));
+}
+
+static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb)
+{
+	struct rtase_int_vector *ivec = ring->ivec;
+
+	napi_gro_receive(&ivec->napi, skb);
+}
+
+static int rx_handler(struct rtase_ring *ring, int budget)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	u32 pkt_size, cur_rx, delta, entry, status;
+	struct net_device *dev = tp->dev;
+	union rx_desc *desc_base = ring->desc;
+	struct sk_buff *skb;
+	union rx_desc *desc;
+	int workdone = 0;
+
+	if (!ring->desc)
+		return workdone;
+
+	cur_rx = ring->cur_idx;
+	entry = cur_rx % NUM_DESC;
+	desc = &desc_base[entry];
+
+	do {
+		/* make sure discriptor has been updated */
+		rmb();
+		status = le32_to_cpu(desc->desc_status.opts1);
+
+		if (status & DESC_OWN)
+			break;
+
+		if (unlikely(status & RX_RES)) {
+			if (net_ratelimit())
+				netdev_warn(dev, "Rx ERROR. status = %08x\n",
+					    status);
+
+			dev->stats.rx_errors++;
+
+			if (status & (RX_RWT | RX_RUNT))
+				dev->stats.rx_length_errors++;
+
+			if (status & RX_CRC)
+				dev->stats.rx_crc_errors++;
+
+			if (dev->features & NETIF_F_RXALL)
+				goto process_pkt;
+
+			rtase_mark_to_asic(desc, tp->rx_buf_sz);
+			goto skip_process_pkt;
+		}
+
+process_pkt:
+		pkt_size = status & RX_PKT_SIZE_MASK;
+		if (likely(!(dev->features & NETIF_F_RXFCS)))
+			pkt_size -= ETH_FCS_LEN;
+
+		/* the driver does not support incoming fragmented
+		 * frames. they are seen as a symptom of over-mtu
+		 * sized frames
+		 */
+		if (unlikely(rtase_fragmented_frame(status))) {
+			dev->stats.rx_dropped++;
+			dev->stats.rx_length_errors++;
+			rtase_mark_to_asic(desc, tp->rx_buf_sz);
+			continue;
+		}
+
+		skb = ring->skbuff[entry];
+		dma_sync_single_for_cpu(&tp->pdev->dev,
+					ring->mis.data_phy_addr[entry],
+					tp->rx_buf_sz, DMA_FROM_DEVICE);
+
+		ring->skbuff[entry] = NULL;
+
+		if (dev->features & NETIF_F_RXCSUM)
+			rtase_rx_csum(tp, skb, desc);
+
+		skb->dev = dev;
+		skb_put(skb, pkt_size);
+		skb_mark_for_recycle(skb);
+		skb->protocol = eth_type_trans(skb, dev);
+
+		if (skb->pkt_type == PACKET_MULTICAST)
+			dev->stats.multicast++;
+
+		rtase_rx_vlan_skb(desc, skb);
+		rtase_rx_skb(ring, skb);
+
+		dev->stats.rx_bytes += pkt_size;
+		dev->stats.rx_packets++;
+
+skip_process_pkt:
+		workdone++;
+		cur_rx++;
+		entry = cur_rx % NUM_DESC;
+		desc = ring->desc + sizeof(union rx_desc) * entry;
+		prefetch(desc);
+	} while (workdone != budget);
+
+	ring->cur_idx = cur_rx;
+	delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx, 1);
+
+	if (!delta && workdone)
+		netdev_info(dev, "no Rx buffer allocated\n");
+
+	ring->dirty_idx += delta;
+
+	if ((ring->dirty_idx + NUM_DESC) == ring->cur_idx)
+		netdev_emerg(dev, "Rx buffers exhausted\n");
+
+	return workdone;
+}
+
+static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
+{
+	struct rtase_ring *ring = &tp->rx_ring[idx];
+	u16 i;
+
+	memset(ring->desc, 0x0, RTASE_RX_RING_DESC_SIZE);
+	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
+	ring->cur_idx = 0;
+	ring->dirty_idx = 0;
+	ring->index = idx;
+
+	for (i = 0; i < NUM_DESC; i++)
+		ring->mis.data_phy_addr[i] = 0;
+
+	ring->ring_handler = rx_handler;
+	ring->ivec = &tp->int_vector[idx];
+	list_add_tail(&ring->ring_entry, &tp->int_vector[idx].ring_list);
+}
+
+static void rtase_rx_clear(struct rtase_private *tp)
+{
+	u32 i;
+
+	for (i = 0; i < tp->func_rx_queue_num; i++)
+		rtase_rx_ring_clear(&tp->rx_ring[i]);
+
+	page_pool_destroy(tp->page_pool);
+	tp->page_pool = NULL;
+}
+
+static int rtase_init_ring(const struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct page_pool *page_pool;
+	struct page_pool_params pp_params = {
+		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+		.order = 0,
+		.pool_size = NUM_DESC * tp->func_rx_queue_num,
+		.nid = dev_to_node(&tp->pdev->dev),
+		.dev = &tp->pdev->dev,
+		.dma_dir = DMA_FROM_DEVICE,
+		.max_len = PAGE_SIZE,
+		.offset = 0,
+	};
+	u32 num;
+	u16 i;
+
+	page_pool = page_pool_create(&pp_params);
+	if (IS_ERR(page_pool)) {
+		netdev_err(tp->dev, "failed to create page pool\n");
+		return -ENOMEM;
+	}
+
+	tp->page_pool = page_pool;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++)
+		rtase_tx_desc_init(tp, i);
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		rtase_rx_desc_init(tp, i);
+		num = rtase_rx_ring_fill(&tp->rx_ring[i], 0, NUM_DESC, 0);
+		if (num != NUM_DESC)
+			goto err_out;
+
+		rtase_mark_as_last_descriptor(tp->rx_ring[i].desc +
+					      sizeof(union rx_desc) *
+					      (NUM_DESC - 1));
+	}
+
+	return 0;
+
+err_out:
+	rtase_rx_clear(tp);
+	return -ENOMEM;
+}
+
+static void rtase_interrupt_mitigation(const struct rtase_private *tp)
+{
+	u32 i;
+
+	/* tx interrupt mitigation */
+	for (i = 0; i < tp->func_tx_queue_num; i++)
+		rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
+
+	/* rx interrupt mitigation */
+	for (i = 0; i < tp->func_rx_queue_num; i++)
+		rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
+}
+
+static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
+{
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
+	rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
+}
+
+static void rtase_tally_counter_clear(const struct rtase_private *tp)
+{
+	u32 cmd = lower_32_bits(tp->tally_paddr);
+
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
+	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
+}
+
+static void rtase_desc_addr_fill(const struct rtase_private *tp)
+{
+	const struct rtase_ring *ring;
+	u16 i, cmd, val;
+	int err;
+
+	for (i = 0; i < tp->func_tx_queue_num; i++) {
+		ring = &tp->tx_ring[i];
+
+		rtase_w32(tp, RTASE_TX_DESC_ADDR0,
+			  lower_32_bits(ring->phy_addr));
+		rtase_w32(tp, RTASE_TX_DESC_ADDR4,
+			  upper_32_bits(ring->phy_addr));
+
+		cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
+		rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
+
+		err = read_poll_timeout(rtase_r16, val, !(val & TX_DESC_CMD_CS),
+					10, 1000, false, tp, RTASE_TX_DESC_COMMAND);
+
+		if (err == -ETIMEDOUT)
+			netdev_err(tp->dev, "error occurred in fill tx descriptor\n");
+	}
+
+	for (i = 0; i < tp->func_rx_queue_num; i++) {
+		ring = &tp->rx_ring[i];
+
+		if (i == 0) {
+			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
+				  lower_32_bits(ring->phy_addr));
+			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
+				  upper_32_bits(ring->phy_addr));
+		} else {
+			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i - 1) * 8)),
+				  lower_32_bits(ring->phy_addr));
+			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i - 1) * 8)),
+				  upper_32_bits(ring->phy_addr));
+		}
+	}
+}
+
+static void rtase_hw_set_features(const struct net_device *dev,
+				  netdev_features_t features)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rx_config, val;
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	if (features & NETIF_F_RXALL)
+		rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
+	else
+		rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
+
+	val = rtase_r16(tp, RTASE_CPLUS_CMD);
+	if (features & NETIF_F_RXCSUM)
+		rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
+	else
+		rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
+		rx_config |= (INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
+	else
+		rx_config &= ~(INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
+}
+
+static void rtase_set_mar(const struct rtase_private *tp)
+{
+	rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
+	rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
+}
+
+static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
+	u16 rx_mode;
+
+	rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
+	rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
+
+	if (dev->flags & IFF_PROMISC) {
+		rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
+	} else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
+		   (dev->flags & IFF_ALLMULTI)) {
+		/* too many to filter perfectly -- accept all multicasts */
+		rx_mode |= ACCEPT_MULTICAST;
+	} else {
+		struct netdev_hw_addr *hw_addr;
+
+		mc_filter[0] = 0;
+		mc_filter[1] = 0;
+
+		netdev_for_each_mc_addr(hw_addr, dev) {
+			u32 bit_nr = eth_hw_addr_crc(hw_addr);
+			u32 idx = u32_get_bits(bit_nr, BIT(31));
+			u32 bit = u32_get_bits(bit_nr, MULTICAST_FILTER_MASK);
+
+			mc_filter[idx] |= BIT(bit);
+			rx_mode |= ACCEPT_MULTICAST;
+		}
+	}
+
+	if (dev->features & NETIF_F_RXALL)
+		rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
+
+	tp->mc_filter[0] = swab32(mc_filter[1]);
+	tp->mc_filter[1] = swab32(mc_filter[0]);
+
+	rtase_set_mar(tp);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
+}
+
+static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
+{
+	const struct rtase_int_vector *ivec = &tp->int_vector[0];
+	u32 val1;
+	u16 val2;
+	u8 i;
+
+	rtase_w32(tp, ivec->imr_addr, 0);
+	val1 = rtase_r32(tp, ivec->isr_addr);
+	rtase_w32(tp, ivec->isr_addr, val1);
+
+	for (i = 1; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		rtase_w16(tp, ivec->imr_addr, 0);
+		val2 = rtase_r16(tp, ivec->isr_addr);
+		rtase_w16(tp, ivec->isr_addr, val2);
+	}
+}
+
+static void rtase_poll_timeout(const struct rtase_private *tp, u32 cond,
+			       u32 sleep_us, u64 timeout_us, u16 reg)
+{
+	int err;
+	u8 val;
+
+	err = read_poll_timeout(rtase_r8, val, val & cond, sleep_us, timeout_us,
+				false, tp, reg);
+
+	if (err == -ETIMEDOUT)
+		netdev_err(tp->dev, "poll reg 0x00%x timeout\n", reg);
+}
+
+static void rtase_nic_reset(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rx_config;
+	u8 val;
+
+	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config & ~ACCEPT_MASK);
+
+	val = rtase_r8(tp, RTASE_MISC);
+	rtase_w8(tp, RTASE_MISC, val | RX_DV_GATE_EN);
+
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val | STOP_REQ);
+	mdelay(2);
+
+	rtase_poll_timeout(tp, STOP_REQ_DONE, 100, 150000, RTASE_CHIP_CMD);
+
+	rtase_poll_timeout(tp, TX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
+
+	rtase_poll_timeout(tp, RX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
+
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val & ~(TE | RE));
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val & ~STOP_REQ);
+
+	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
+}
+
+static void rtase_hw_reset(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_irq_dis_and_clear(tp);
+
+	rtase_nic_reset(dev);
+}
+
+static void rtase_set_rx_queue(const struct rtase_private *tp)
+{
+	u16 reg_data;
+
+	reg_data = rtase_r16(tp, RTASE_FCR);
+	switch (tp->func_rx_queue_num) {
+	case 1:
+		u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
+		break;
+	case 2:
+		u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
+		break;
+	case 4:
+		u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
+		break;
+	}
+	rtase_w16(tp, RTASE_FCR, reg_data);
+}
+
+static void rtase_set_tx_queue(const struct rtase_private *tp)
+{
+	u16 reg_data;
+
+	reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
+	switch (tp->tx_queue_ctrl) {
+	case 1:
+		u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
+		break;
+	case 2:
+		u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
+		break;
+	case 3:
+	case 4:
+		u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
+		break;
+	default:
+		u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
+		break;
+	}
+	rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
+}
+
+static void rtase_hw_config(struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u32 reg_data32;
+	u16 reg_data16;
+
+	rtase_hw_reset(dev);
+
+	/* Set Rx DMA burst */
+	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
+	reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
+	u16p_replace_bits(&reg_data16, RX_DMA_BURST_256, RX_MX_DMA_MASK);
+	rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
+
+	/* New Rx Descritpor */
+	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
+	u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
+	rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
+
+	rtase_set_rx_queue(tp);
+
+	/* interrupt mitigation */
+	rtase_interrupt_mitigation(tp);
+
+	/* set tx DMA burst size and interframe gap time */
+	reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
+	u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED, TX_DMA_MASK);
+	u32p_replace_bits(&reg_data32, INTERFRAMEGAP, TX_INTER_FRAME_GAP_MASK);
+	rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
+
+	/* new tx Descriptor */
+	reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
+	rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 | TX_NEW_DESC_FORMAT_EN);
+
+	/* tx Fetch Desc Number */
+	rtase_w8(tp, RTASE_TDFNR, 0x10);
+
+	/* tag num select */
+	reg_data16 = rtase_r16(tp, RTASE_MTPS);
+	u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
+	rtase_w16(tp, RTASE_MTPS, reg_data16);
+
+	rtase_set_tx_queue(tp);
+
+	/* TOK condition */
+	rtase_w16(tp, RTASE_TOKSEL, 0x5555);
+
+	rtase_tally_counter_addr_fill(tp);
+	rtase_desc_addr_fill(tp);
+	rtase_hw_set_features(dev, dev->features);
+
+	/* enable flow control */
+	reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
+	reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
+	rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
+	/* set Near FIFO Threshold - rx missed issue. */
+	rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
+
+	rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
+
+	/* set Rx packet filter */
+	rtase_hw_set_rx_packet_filter(dev);
+}
+
+static void rtase_nic_enable(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 rcr = rtase_r16(tp, RTASE_RX_CONFIG_1);
+	u8 val;
+
+	/* PCIe PLA reload */
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr & ~PCIE_RELOAD_En);
+	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr | PCIE_RELOAD_En);
+
+	/* set PCIe TE & RE */
+	val = rtase_r8(tp, RTASE_CHIP_CMD);
+	rtase_w8(tp, RTASE_CHIP_CMD, val | TE | RE);
+
+	/* clear rxdv_gated_en */
+	val = rtase_r8(tp, RTASE_MISC);
+	rtase_w8(tp, RTASE_MISC, val & ~RX_DV_GATE_EN);
+}
+
+static void rtase_enable_hw_interrupt(const struct rtase_private *tp)
+{
+	const struct rtase_int_vector *ivec = &tp->int_vector[0];
+	u32 i;
+
+	rtase_w32(tp, ivec->imr_addr, ivec->imr);
+
+	for (i = 1; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		rtase_w16(tp, ivec->imr_addr, ivec->imr);
+	}
+}
+
+static void rtase_hw_start(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_nic_enable(dev);
+	rtase_enable_hw_interrupt(tp);
+}
+
+/*  the interrupt handler does RXQ0 and TXQ0, TXQ4~7 interrutp status
+ */
+static irqreturn_t rtase_interrupt(int irq, void *dev_instance)
+{
+	const struct rtase_private *tp;
+	struct rtase_int_vector *ivec;
+	u32 status;
+
+	ivec = dev_instance;
+	tp = ivec->tp;
+	status = rtase_r32(tp, ivec->isr_addr);
+
+	rtase_w32(tp, ivec->imr_addr, 0x0);
+	rtase_w32(tp, ivec->isr_addr, status & ~FOVW);
+
+	if (napi_schedule_prep(&ivec->napi))
+		__napi_schedule(&ivec->napi);
+
+	return IRQ_HANDLED;
+}
+
+/*  the interrupt handler does RXQ1&TXQ1 or RXQ2&TXQ2 or RXQ3&TXQ3 interrupt
+ *  status according to interrupt vector
+ */
+static irqreturn_t rtase_q_interrupt(int irq, void *dev_instance)
+{
+	const struct rtase_private *tp;
+	struct rtase_int_vector *ivec;
+	u16 status;
+
+	ivec = dev_instance;
+	tp = ivec->tp;
+	status = rtase_r16(tp, ivec->isr_addr);
+
+	rtase_w16(tp, ivec->imr_addr, 0x0);
+	rtase_w16(tp, ivec->isr_addr, status);
+
+	if (napi_schedule_prep(&ivec->napi))
+		__napi_schedule(&ivec->napi);
+
+	return IRQ_HANDLED;
+}
+
+static int rtase_poll(struct napi_struct *napi, int budget)
+{
+	const struct rtase_int_vector *ivec;
+	const struct rtase_private *tp;
+	struct rtase_ring *ring;
+	int total_workdone = 0;
+
+	ivec = container_of(napi, struct rtase_int_vector, napi);
+	tp = ivec->tp;
+
+	list_for_each_entry(ring, &ivec->ring_list, ring_entry) {
+		total_workdone += ring->ring_handler(ring, budget);
+	}
+
+	if (total_workdone >= budget)
+		return budget;
+
+	if (napi_complete_done(napi, total_workdone)) {
+		if (!ivec->index)
+			rtase_w32(tp, ivec->imr_addr, ivec->imr);
+		else
+			rtase_w16(tp, ivec->imr_addr, ivec->imr);
+	}
+
+	return total_workdone;
+}
+
+static int rtase_open(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec = &tp->int_vector[0];
+	const struct pci_dev *pdev = tp->pdev;
+	int ret;
+	u16 i;
+
+	rtase_set_rxbufsize(tp);
+
+	ret = rtase_alloc_desc(tp);
+	if (ret)
+		goto err_free_all_allocated_mem;
+
+	ret = rtase_init_ring(dev);
+	if (ret)
+		goto err_free_all_allocated_mem;
+
+	INIT_DELAYED_WORK(&tp->task, NULL);
+
+	rtase_hw_config(dev);
+
+	if (tp->sw_flag & SWF_MSIX_ENABLED) {
+		ret = request_irq(ivec->irq, rtase_interrupt, 0,
+				  dev->name, ivec);
+
+		/* request other interrupts to handle multiqueue */
+		for (i = 1; i < tp->int_nums; i++) {
+			if (ret)
+				continue;
+
+			ivec = &tp->int_vector[i];
+			if (ivec->status != 1)
+				continue;
+
+			snprintf(ivec->name, sizeof(ivec->name), "%s_int%i", tp->dev->name, i);
+			ret = request_irq(ivec->irq, rtase_q_interrupt, 0,
+					  ivec->name, ivec);
+		}
+	} else if (tp->sw_flag & SWF_MSI_ENABLED) {
+		ret = request_irq(pdev->irq, rtase_interrupt, 0, dev->name,
+				  ivec);
+	} else {
+		ret = request_irq(pdev->irq, rtase_interrupt, IRQF_SHARED,
+				  dev->name, ivec);
+	}
+
+	if (ret != 0) {
+		netdev_err(dev, "can't request MSIX interrupt. Error: %d\n", ret);
+		goto err_free_all_allocated_mem;
+	}
+
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+
+	goto out;
+
+err_free_all_allocated_mem:
+	rtase_free_desc(tp);
+
+out:
+	return ret;
+}
+
+static void rtase_down(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 i;
+
+	netif_stop_queue(dev);
+
+	/* give a racing hard_start_xmit a few cycles to complete */
+	synchronize_rcu();
+
+	netif_carrier_off(dev);
+
+	rtase_hw_reset(dev);
+
+	for (i = 0; i < tp->int_nums; i++)
+		synchronize_irq(tp->int_vector[i].irq);
+
+	rtase_tx_clear(tp);
+
+	rtase_rx_clear(tp);
+}
+
+static int rtase_close(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	const struct pci_dev *pdev = tp->pdev;
+	u32 i;
+
+	rtase_down(dev);
+
+	if (tp->sw_flag & SWF_MSIX_ENABLED) {
+		for (i = 0; i < tp->int_nums; i++)
+			free_irq(tp->int_vector[i].irq, &tp->int_vector[i]);
+
+	} else {
+		free_irq(pdev->irq, &tp->int_vector[0]);
+	}
+
+	rtase_free_desc(tp);
+
+	return 0;
+}
+
+static u32 rtase_tx_vlan_tag(const struct rtase_private *tp,
+			     const struct sk_buff *skb)
+{
+	return (skb_vlan_tag_present(skb)) ?
+		(TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00;
+}
+
+static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
+{
+	u8 ip_protocol;
+	u32 csum_cmd;
+
+	switch (vlan_get_protocol(skb)) {
+	case htons(ETH_P_IP):
+		csum_cmd = TX_IPCS_C;
+		ip_protocol = ip_hdr(skb)->protocol;
+		break;
+
+	case htons(ETH_P_IPV6):
+		csum_cmd = TX_IPV6F_C;
+		ip_protocol = ipv6_hdr(skb)->nexthdr;
+		break;
+
+	default:
+		ip_protocol = IPPROTO_RAW;
+		break;
+	}
+
+	if (ip_protocol == IPPROTO_TCP)
+		csum_cmd |= TX_TCPCS_C;
+	else if (ip_protocol == IPPROTO_UDP)
+		csum_cmd |= TX_UDPCS_C;
+	else
+		WARN_ON_ONCE(1);
+
+	csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+
+	return csum_cmd;
+}
+
+static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
+			    u32 opts1, u32 opts2)
+{
+	const struct skb_shared_info *info = skb_shinfo(skb);
+	const struct rtase_private *tp = ring->ivec->tp;
+	const u8 nr_frags = info->nr_frags;
+	struct tx_desc *txd = NULL;
+	u32 cur_frag, entry;
+	u64 pkt_len_cnt = 0;
+
+	entry = ring->cur_idx;
+	for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
+		const skb_frag_t *frag = &info->frags[cur_frag];
+		dma_addr_t mapping;
+		u32 status, len;
+		void *addr;
+
+		entry = (entry + 1) % NUM_DESC;
+
+		txd = ring->desc + sizeof(struct tx_desc) * entry;
+		len = skb_frag_size(frag);
+		addr = skb_frag_address(frag);
+		mapping = dma_map_single(&tp->pdev->dev, addr, len,
+					 DMA_TO_DEVICE);
+
+		if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+			if (unlikely(net_ratelimit()))
+				netdev_err(tp->dev,
+					   "Failed to map TX fragments DMA!\n");
+
+			goto err_out;
+		}
+
+		if (((entry + 1) % NUM_DESC) == 0)
+			status = (opts1 | len | RING_END);
+		else
+			status = opts1 | len;
+
+		if (cur_frag == (nr_frags - 1)) {
+			ring->skbuff[entry] = skb;
+			status |= TX_LAST_FRAG;
+		}
+
+		ring->mis.len[entry] = len;
+		txd->addr = cpu_to_le64(mapping);
+		txd->opts2 = cpu_to_le32(opts2);
+
+		/* make sure the operating fields have been updated */
+		wmb();
+		txd->opts1 = cpu_to_le32(status);
+		pkt_len_cnt += len;
+	}
+
+	return cur_frag;
+
+err_out:
+	rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
+	return -EIO;
+}
+
+static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+				    struct net_device *dev)
+{
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 q_idx, entry, len, opts1, opts2;
+	u32 mss = shinfo->gso_size;
+	struct rtase_ring *ring;
+	struct tx_desc *txd;
+	dma_addr_t mapping;
+	bool stop_queue;
+	int frags;
+
+	/* multiqueues */
+	q_idx = skb_get_queue_mapping(skb);
+	ring = &tp->tx_ring[q_idx];
+
+	if (unlikely(!rtase_tx_avail(ring))) {
+		if (net_ratelimit())
+			netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+		goto err_stop;
+	}
+
+	entry = ring->cur_idx % NUM_DESC;
+	txd = ring->desc + sizeof(struct tx_desc) * entry;
+
+	opts1 = DESC_OWN;
+	opts2 = rtase_tx_vlan_tag(tp, skb);
+
+	/* tcp segmentation offload (or tcp large send) */
+	if (mss) {
+		if (shinfo->gso_type & SKB_GSO_TCPV4) {
+			opts1 |= GIANT_SEND_V4;
+		} else if (shinfo->gso_type & SKB_GSO_TCPV6) {
+			if (skb_cow_head(skb, 0))
+				goto err_dma_0;
+
+			tcp_v6_gso_csum_prep(skb);
+			opts1 |= GIANT_SEND_V6;
+		} else {
+			WARN_ON_ONCE(1);
+		}
+
+		opts1 |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+		opts2 |= u32_encode_bits(mss, MSS_MASK);
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		opts2 |= rtase_tx_csum(skb, dev);
+	}
+
+	frags = rtase_xmit_frags(ring, skb, opts1, opts2);
+	if (unlikely(frags < 0))
+		goto err_dma_0;
+
+	if (frags) {
+		len = skb_headlen(skb);
+		opts1 |= TX_FIRST_FRAG;
+	} else {
+		len = skb->len;
+		ring->skbuff[entry] = skb;
+		opts1 |= TX_FIRST_FRAG | TX_LAST_FRAG;
+	}
+
+	if (((entry + 1) % NUM_DESC) == 0)
+		opts1 |= (len | RING_END);
+	else
+		opts1 |= len;
+
+	mapping = dma_map_single(&tp->pdev->dev, skb->data, len,
+				 DMA_TO_DEVICE);
+
+	if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+		if (unlikely(net_ratelimit()))
+			netdev_err(dev, "Failed to map TX DMA!\n");
+
+		goto err_dma_1;
+	}
+
+	ring->mis.len[entry] = len;
+	txd->addr = cpu_to_le64(mapping);
+	txd->opts2 = cpu_to_le32(opts2);
+	txd->opts1 = cpu_to_le32(opts1 & ~DESC_OWN);
+
+	/* make sure the operating fields have been updated */
+	wmb();
+
+	txd->opts1 = cpu_to_le32(opts1);
+
+	skb_tx_timestamp(skb);
+
+	/* tx needs to see descriptor changes before updated cur_idx */
+	smp_wmb();
+
+	WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1);
+
+	stop_queue = !rtase_tx_avail(ring);
+	if (unlikely(stop_queue))
+		netif_stop_subqueue(dev, q_idx);
+
+	/* set polling bit */
+	rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+
+	if (unlikely(stop_queue)) {
+		/* make sure cur_idx and dirty_idx have been updated */
+		smp_rmb();
+		if (rtase_tx_avail(ring))
+			netif_start_subqueue(dev, q_idx);
+	}
+
+	return NETDEV_TX_OK;
+
+err_dma_1:
+	ring->skbuff[entry] = NULL;
+	rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
+
+err_dma_0:
+	dev->stats.tx_dropped++;
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+
+err_stop:
+	netif_stop_queue(dev);
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_BUSY;
+}
+
+static void rtase_set_rx_mode(struct net_device *dev)
+{
+	rtase_hw_set_rx_packet_filter(dev);
+}
+
+static void rtase_enable_eem_write(const struct rtase_private *tp)
+{
+	u8 val;
+
+	val = rtase_r8(tp, RTASE_EEM);
+	rtase_w8(tp, RTASE_EEM, val | EEM_UNLOCK);
+}
+
+static void rtase_disable_eem_write(const struct rtase_private *tp)
+{
+	u8 val;
+
+	val = rtase_r8(tp, RTASE_EEM);
+	rtase_w8(tp, RTASE_EEM, val & ~EEM_UNLOCK);
+}
+
+static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
+{
+	u32 rar_low, rar_high;
+
+	rar_low = (u32)addr[0] | ((u32)addr[1] << 8) |
+		  ((u32)addr[2] << 16) | ((u32)addr[3] << 24);
+
+	rar_high = (u32)addr[4] | ((u32)addr[5] << 8);
+
+	rtase_enable_eem_write(tp);
+	rtase_w32(tp, RTASE_MAC0, rar_low);
+	rtase_w32(tp, RTASE_MAC4, rar_high);
+	rtase_disable_eem_write(tp);
+	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
+}
+
+static int rtase_set_mac_address(struct net_device *dev, void *p)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	ret = eth_mac_addr(dev, p);
+	if (ret)
+		return ret;
+
+	rtase_rar_set(tp, dev->dev_addr);
+
+	return 0;
+}
+
+static int rtase_change_mtu(struct net_device *dev, int new_mtu)
+{
+	dev->mtu = new_mtu;
+
+	netdev_update_features(dev);
+
+	return 0;
+}
+
+static void rtase_wait_for_quiescence(const struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec;
+	u32 i;
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		synchronize_irq(ivec->irq);
+		/* wait for any pending NAPI task to complete */
+		napi_disable(&ivec->napi);
+	}
+
+	rtase_irq_dis_and_clear(tp);
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		napi_enable(&ivec->napi);
+	}
+}
+
+static void rtase_sw_reset(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+	rtase_hw_reset(dev);
+
+	/* let's wait a bit while any (async) irq lands on */
+	rtase_wait_for_quiescence(dev);
+	rtase_tx_clear(tp);
+	rtase_rx_clear(tp);
+
+	ret = rtase_init_ring(dev);
+	if (ret) {
+		netdev_err(dev, "unable to init ring\n");
+		rtase_free_desc(tp);
+		return;
+	}
+
+	rtase_hw_config(dev);
+	/* always link, so start to transmit & receive */
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+}
+
+static void rtase_dump_tally_counter(const struct rtase_private *tp)
+{
+	dma_addr_t paddr = tp->tally_paddr;
+	u32 cmd = lower_32_bits(paddr);
+	u32 val;
+	int err;
+
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(paddr));
+	rtase_w32(tp, RTASE_DTCCR0, cmd);
+	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_DUMP);
+
+	err = read_poll_timeout(rtase_r32, val, !(val & COUNTER_DUMP), 10, 250,
+				false, tp, RTASE_DTCCR0);
+
+	if (err == -ETIMEDOUT)
+		netdev_err(tp->dev, "error occurred in dump tally counter\n");
+}
+
+static void rtase_dump_state(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters;
+	int max_reg_size = RTASE_PCI_REGS_SIZE;
+	const struct rtase_ring *ring;
+	u32 dword_rd;
+	int n = 0;
+
+	ring = &tp->tx_ring[0];
+	netdev_err(dev, "Tx descriptor info:\n");
+	netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
+	netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
+	netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
+
+	ring = &tp->rx_ring[0];
+	netdev_err(dev, "Rx descriptor info:\n");
+	netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
+	netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
+	netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
+
+	netdev_err(dev, "Device Registers:\n");
+	netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp, RTASE_CHIP_CMD));
+	netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
+	netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
+	netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
+		   rtase_r16(tp, RTASE_BOOT_CTL));
+	netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
+		   rtase_r16(tp, RTASE_EPHY_ISR));
+	netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
+		   rtase_r16(tp, RTASE_EPHY_IMR));
+	netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
+		   rtase_r16(tp, RTASE_CLKSW_SET));
+
+	netdev_err(dev, "Dump PCI Registers:\n");
+
+	while (n < max_reg_size) {
+		if ((n % RTASE_DWORD_MOD) == 0)
+			netdev_err(tp->dev, "0x%03x:\n", n);
+
+		pci_read_config_dword(tp->pdev, n, &dword_rd);
+		netdev_err(tp->dev, "%08x\n", dword_rd);
+		n += 4;
+	}
+
+	netdev_err(dev, "Dump tally counter:\n");
+	counters = tp->tally_vaddr;
+	rtase_dump_tally_counter(tp);
+
+	netdev_err(dev, "tx_packets %lld\n",
+		   le64_to_cpu(counters->tx_packets));
+	netdev_err(dev, "rx_packets %lld\n",
+		   le64_to_cpu(counters->rx_packets));
+	netdev_err(dev, "tx_errors %lld\n",
+		   le64_to_cpu(counters->tx_errors));
+	netdev_err(dev, "rx_missed %lld\n",
+		   le64_to_cpu(counters->rx_missed));
+	netdev_err(dev, "align_errors %lld\n",
+		   le64_to_cpu(counters->align_errors));
+	netdev_err(dev, "tx_one_collision %lld\n",
+		   le64_to_cpu(counters->tx_one_collision));
+	netdev_err(dev, "tx_multi_collision %lld\n",
+		   le64_to_cpu(counters->tx_multi_collision));
+	netdev_err(dev, "rx_unicast %lld\n",
+		   le64_to_cpu(counters->rx_unicast));
+	netdev_err(dev, "rx_broadcast %lld\n",
+		   le64_to_cpu(counters->rx_broadcast));
+	netdev_err(dev, "rx_multicast %lld\n",
+		   le64_to_cpu(counters->rx_multicast));
+	netdev_err(dev, "tx_aborted %lld\n",
+		   le64_to_cpu(counters->tx_aborted));
+	netdev_err(dev, "tx_underun %lld\n",
+		   le64_to_cpu(counters->tx_underun));
+}
+
+static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+	rtase_dump_state(dev);
+	rtase_sw_reset(dev);
+}
+
+static void rtase_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *stats)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters = tp->tally_vaddr;
+
+	if (!counters)
+		return;
+
+	netdev_stats_to_stats64(stats, &dev->stats);
+	dev_fetch_sw_netstats(stats, dev->tstats);
+
+	/* fetch additional counter values missing in stats collected by driver
+	 * from tally counter
+	 */
+	rtase_dump_tally_counter(tp);
+
+	stats->tx_errors = le64_to_cpu(counters->tx_errors);
+	stats->collisions = le32_to_cpu(counters->tx_multi_collision);
+	stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted);
+	stats->rx_missed_errors = le16_to_cpu(counters->rx_missed);
+}
+
+static void rtase_enable_vlan_filter(const struct rtase_private *tp, bool enabled)
+{
+	u16 tmp;
+
+	if (enabled == 1) {
+		tmp = rtase_r16(tp, RTASE_FCR);
+		if (!(tmp & FCR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_FCR, tmp | FCR_VLAN_FTR_EN);
+
+		tmp = rtase_r16(tp, RTASE_PCPR);
+		if (!(tmp & PCPR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_PCPR, tmp | PCPR_VLAN_FTR_EN);
+	} else {
+		tmp = rtase_r16(tp, RTASE_FCR);
+		if (tmp & FCR_VLAN_FTR_EN)
+			rtase_w16(tp, RTASE_FCR, tmp & ~FCR_VLAN_FTR_EN);
+
+		tmp = rtase_r16(tp, RTASE_PCPR);
+		if (!(tmp & PCPR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_PCPR, tmp & ~PCPR_VLAN_FTR_EN);
+	}
+}
+
+static int rtase_vlan_rx_add_vid(struct net_device *dev, __be16 protocol,
+				 u16 vid)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u16 tmp_mem, i;
+
+	if (be16_to_cpu(protocol) != ETH_P_8021Q)
+		return -EINVAL;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+		u16 addr, mask;
+
+		if (!(tp->vlan_filter_ctrl & BIT(i))) {
+			tp->vlan_filter_ctrl |= BIT(i);
+			tp->vlan_filter_vid[i] = vid;
+			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4,
+				  vid | VLAN_ENTRY_CAREBIT);
+			/* each 16-bit register contains two VLAN entries */
+			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+			mask = 0x1 << ((i & 0x1) * 8);
+			tmp_mem = rtase_r16(tp, addr);
+			tmp_mem |= mask;
+			rtase_w16(tp, addr, tmp_mem);
+			break;
+		}
+	}
+
+	if (i == RTASE_VLAN_FILTER_ENTRY_NUM)
+		return -ENOENT;
+
+	rtase_enable_vlan_filter(tp, true);
+
+	return 0;
+}
+
+static int rtase_vlan_rx_kill_vid(struct net_device *dev, __be16 protocol,
+				  u16 vid)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u16 tmp_mem, i;
+
+	if (be16_to_cpu(protocol) != ETH_P_8021Q)
+		return -EINVAL;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+		u16 addr, mask;
+
+		if (tp->vlan_filter_vid[i] == vid) {
+			tp->vlan_filter_ctrl &= ~BIT(i);
+			tp->vlan_filter_vid[i] = 0;
+			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
+
+			/* each 16-bit register contains two VLAN entries */
+			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+			mask = ~(0x1 << ((i & 0x1) * 8));
+			tmp_mem = rtase_r16(tp, addr);
+			tmp_mem &= mask;
+			rtase_w16(tp, addr, tmp_mem);
+			break;
+		}
+	}
+
+	/* check vlan filter enabled */
+	if (!tp->vlan_filter_ctrl)
+		rtase_enable_vlan_filter(tp, false);
+
+	return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/* Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void rtase_netpoll(struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct pci_dev *pdev = tp->pdev;
+
+	disable_irq(pdev->irq);
+	rtase_interrupt(pdev->irq, dev);
+	enable_irq(pdev->irq);
+}
+#endif
+
+static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue)
+{
+	u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK;
+	u32 val, i;
+
+	val = u32_encode_bits(idle / RTASE_1T_POWER, RTASE_IDLESLOPE_INT_MASK);
+	idle %= RTASE_1T_POWER;
+
+	for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) {
+		idle *= 2;
+		if ((idle / RTASE_1T_POWER) == 1)
+			val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i);
+
+		idle %= RTASE_1T_POWER;
+	}
+	rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val);
+}
+
+static void rtase_setup_tc_cbs(struct rtase_private *tp,
+			       const struct tc_cbs_qopt_offload *qopt)
+{
+	u32 queue = qopt->queue;
+
+	tp->tx_qos[queue].hicredit = qopt->hicredit;
+	tp->tx_qos[queue].locredit = qopt->locredit;
+	tp->tx_qos[queue].idleslope = qopt->idleslope;
+	tp->tx_qos[queue].sendslope = qopt->sendslope;
+
+	/* set hardware cbs */
+	rtase_set_hw_cbs(tp, queue);
+}
+
+static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type,
+			  void *type_data)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+
+	switch (type) {
+	case TC_SETUP_QDISC_CBS:
+		rtase_setup_tc_cbs(tp, type_data);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static netdev_features_t rtase_fix_features(struct net_device *dev,
+					    netdev_features_t features)
+{
+	netdev_features_t features_fix = features;
+
+	if (dev->mtu > MSS_MAX)
+		features_fix &= ~NETIF_F_ALL_TSO;
+
+	if (dev->mtu > ETH_DATA_LEN)
+		features_fix &= ~NETIF_F_ALL_TSO;
+
+	return features_fix;
+}
+
+static int rtase_set_features(struct net_device *dev,
+			      netdev_features_t features)
+{
+	netdev_features_t features_set = features;
+
+	features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM |
+			NETIF_F_HW_VLAN_CTAG_RX;
+
+	if (features_set ^ dev->features)
+		rtase_hw_set_features(dev, features_set);
+
+	return 0;
+}
+
+static const struct net_device_ops rtase_netdev_ops = {
+	.ndo_open = rtase_open,
+	.ndo_stop = rtase_close,
+	.ndo_start_xmit = rtase_start_xmit,
+	.ndo_set_rx_mode = rtase_set_rx_mode,
+	.ndo_set_mac_address = rtase_set_mac_address,
+	.ndo_change_mtu = rtase_change_mtu,
+	.ndo_tx_timeout = rtase_tx_timeout,
+	.ndo_get_stats64 = rtase_get_stats64,
+	.ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = rtase_netpoll,
+#endif
+	.ndo_setup_tc = rtase_setup_tc,
+	.ndo_fix_features = rtase_fix_features,
+	.ndo_set_features = rtase_set_features,
+};
+
+static void rtase_get_mac_address(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u8 mac_addr[ETH_ALEN] __aligned(2) = {};
+	u32 i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		mac_addr[i] = rtase_r8(tp, RTASE_MAC0 + i);
+
+	if (!is_valid_ether_addr(mac_addr)) {
+		eth_random_addr(mac_addr);
+		dev->addr_assign_type = NET_ADDR_RANDOM;
+		netdev_warn(dev, "Random ether addr %pM\n", mac_addr);
+	}
+
+	eth_hw_addr_set(dev, mac_addr);
+	rtase_rar_set(tp, mac_addr);
+
+	/* keep the original MAC address */
+	ether_addr_copy(tp->org_mac_addr, dev->dev_addr);
+	ether_addr_copy(dev->perm_addr, dev->dev_addr);
+}
+
+static void rtase_get_drvinfo(struct net_device *dev,
+			      struct ethtool_drvinfo *drvinfo)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
+	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
+}
+
+static int rtase_get_settings(struct net_device *dev,
+			      struct ethtool_link_ksettings *cmd)
+{
+	u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
+						supported);
+	cmd->base.speed = SPEED_5000;
+	cmd->base.duplex = DUPLEX_FULL;
+	cmd->base.port = PORT_MII;
+	cmd->base.autoneg = AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static void rtase_get_pauseparam(struct net_device *dev,
+				 struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	pause->autoneg = AUTONEG_DISABLE;
+
+	if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
+	    (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+		pause->tx_pause = 1;
+	} else if ((value & FORCE_TXFLOW_EN)) {
+		pause->tx_pause = 1;
+	} else if ((value & FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+	}
+}
+
+static int rtase_set_pauseparam(struct net_device *dev,
+				struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	if (pause->autoneg)
+		return -EOPNOTSUPP;
+
+	value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
+
+	if (pause->tx_pause)
+		value |= FORCE_TXFLOW_EN;
+
+	if (pause->rx_pause)
+		value |= FORCE_RXFLOW_EN;
+
+	rtase_w16(tp, RTASE_CPLUS_CMD, value);
+	return 0;
+}
+
+static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
+	"tx_packets",
+	"rx_packets",
+	"tx_errors",
+	"rx_errors",
+	"rx_missed",
+	"align_errors",
+	"tx_single_collisions",
+	"tx_multi_collisions",
+	"unicast",
+	"broadcast",
+	"multicast",
+	"tx_aborted",
+	"tx_underrun",
+};
+
+static void rtase_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
+		break;
+	}
+}
+
+static int rtase_get_sset_count(struct net_device *dev, int sset)
+{
+	int ret = -EOPNOTSUPP;
+
+	switch (sset) {
+	case ETH_SS_STATS:
+		ret = ARRAY_SIZE(rtase_gstrings);
+		break;
+	}
+
+	return ret;
+}
+
+static void rtase_get_ethtool_stats(struct net_device *dev,
+				    struct ethtool_stats *stats, u64 *data)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters;
+
+	ASSERT_RTNL();
+
+	counters = tp->tally_vaddr;
+	if (!counters)
+		return;
+
+	rtase_dump_tally_counter(tp);
+
+	data[0] = le64_to_cpu(counters->tx_packets);
+	data[1] = le64_to_cpu(counters->rx_packets);
+	data[2] = le64_to_cpu(counters->tx_errors);
+	data[3] = le32_to_cpu(counters->rx_errors);
+	data[4] = le16_to_cpu(counters->rx_missed);
+	data[5] = le16_to_cpu(counters->align_errors);
+	data[6] = le32_to_cpu(counters->tx_one_collision);
+	data[7] = le32_to_cpu(counters->tx_multi_collision);
+	data[8] = le64_to_cpu(counters->rx_unicast);
+	data[9] = le64_to_cpu(counters->rx_broadcast);
+	data[10] = le32_to_cpu(counters->rx_multicast);
+	data[11] = le16_to_cpu(counters->tx_aborted);
+	data[12] = le16_to_cpu(counters->tx_underun);
+}
+
+static const struct ethtool_ops rtase_ethtool_ops = {
+	.get_drvinfo = rtase_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+	.get_link_ksettings = rtase_get_settings,
+	.get_pauseparam = rtase_get_pauseparam,
+	.set_pauseparam = rtase_set_pauseparam,
+	.get_strings = rtase_get_strings,
+	.get_sset_count = rtase_get_sset_count,
+	.get_ethtool_stats = rtase_get_ethtool_stats,
+	.get_ts_info = ethtool_op_get_ts_info,
+};
+
+static void rtase_init_netdev_ops(struct net_device *dev)
+{
+	dev->netdev_ops = &rtase_netdev_ops;
+	dev->ethtool_ops = &rtase_ethtool_ops;
+}
+
+static void rtase_reset_interrupt(struct pci_dev *pdev,
+				  const struct rtase_private *tp)
+{
+	if (tp->sw_flag & SWF_MSIX_ENABLED)
+		pci_disable_msix(pdev);
+	else
+		pci_disable_msi(pdev);
+}
+
+static int rtase_alloc_msix(struct pci_dev *pdev, struct rtase_private *tp)
+{
+	int ret;
+	u16 i;
+
+	memset(tp->msix_entry, 0x0, RTASE_NUM_MSIX * sizeof(struct msix_entry));
+
+	for (i = 0; i < RTASE_NUM_MSIX; i++)
+		tp->msix_entry[i].entry = i;
+
+	ret = pci_enable_msix_range(pdev, tp->msix_entry, tp->int_nums,
+				    tp->int_nums);
+
+	if (ret == tp->int_nums) {
+		for (i = 0; i < tp->int_nums; i++) {
+			tp->int_vector[i].irq = pci_irq_vector(pdev, i);
+			tp->int_vector[i].status = 1;
+		}
+	}
+
+	return ret;
+}
+
+static int rtase_alloc_interrupt(struct pci_dev *pdev,
+				 struct rtase_private *tp)
+{
+	int ret;
+
+	ret = rtase_alloc_msix(pdev, tp);
+	if (ret != tp->int_nums) {
+		ret = pci_enable_msi(pdev);
+		if (ret)
+			dev_err(&pdev->dev,
+				"unable to alloc interrupt.(MSI)\n");
+		else
+			tp->sw_flag |= SWF_MSI_ENABLED;
+	} else {
+		tp->sw_flag |= SWF_MSIX_ENABLED;
+	}
+
+	return ret;
+}
+
+static void rtase_init_hardware(const struct rtase_private *tp)
+{
+	u16 i;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++)
+		rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
+}
+
+static void rtase_init_int_vector(struct rtase_private *tp)
+{
+	u16 i;
+
+	/* interrupt vector 0 */
+	tp->int_vector[0].tp = tp;
+	tp->int_vector[0].index = 0;
+	tp->int_vector[0].imr_addr = RTASE_IMR0;
+	tp->int_vector[0].isr_addr = RTASE_ISR0;
+	tp->int_vector[0].imr = ROK | RDU | TOK | TOK4 | TOK5 | TOK6 | TOK7;
+	tp->int_vector[0].poll = rtase_poll;
+
+	memset(tp->int_vector[0].name, 0x0, sizeof(tp->int_vector[0].name));
+	INIT_LIST_HEAD(&tp->int_vector[0].ring_list);
+
+	netif_napi_add(tp->dev, &tp->int_vector[0].napi,
+		       tp->int_vector[0].poll);
+	napi_enable(&tp->int_vector[0].napi);
+
+	/* interrupt vector 1 ~ 3 */
+	for (i = 1; i < tp->int_nums; i++) {
+		tp->int_vector[i].tp = tp;
+		tp->int_vector[i].index = i;
+		tp->int_vector[i].imr_addr = RTASE_IMR1 + (i - 1) * 4;
+		tp->int_vector[i].isr_addr = RTASE_ISR1 + (i - 1) * 4;
+		tp->int_vector[i].imr = Q_ROK | Q_RDU | Q_TOK;
+		tp->int_vector[i].poll = rtase_poll;
+
+		memset(tp->int_vector[i].name, 0x0, sizeof(tp->int_vector[0].name));
+		INIT_LIST_HEAD(&tp->int_vector[i].ring_list);
+
+		netif_napi_add(tp->dev, &tp->int_vector[i].napi,
+			       tp->int_vector[i].poll);
+		napi_enable(&tp->int_vector[i].napi);
+	}
+}
+
+static u16 rtase_calc_time_mitigation(u32 time_us)
+{
+	u16 int_miti;
+	u8 msb, time_count, time_unit;
+
+	time_us = min_t(int, time_us, MITI_MAX_TIME);
+
+	msb = fls(time_us);
+	if (msb >= MITI_COUNT_BIT_NUM) {
+		time_unit = msb - MITI_COUNT_BIT_NUM;
+		time_count = time_us >> (msb - MITI_COUNT_BIT_NUM);
+	} else {
+		time_unit = 0;
+		time_count = time_us;
+	}
+
+	int_miti = u16_encode_bits(time_count, MITI_TIME_COUNT_MASK) |
+		   u16_encode_bits(time_unit, MITI_TIME_UNIT_MASK);
+
+	return int_miti;
+}
+
+static u16 rtase_calc_packet_num_mitigation(u16 pkt_num)
+{
+	u16 int_miti;
+	u8 msb, pkt_num_count, pkt_num_unit;
+
+	pkt_num = min_t(int, pkt_num, MITI_MAX_PKT_NUM);
+
+	if (pkt_num > 60) {
+		pkt_num_unit = MITI_MAX_PKT_NUM_IDX;
+		pkt_num_count = pkt_num / MITI_MAX_PKT_NUM_UNIT;
+	} else {
+		msb = fls(pkt_num);
+		if (msb >= MITI_COUNT_BIT_NUM) {
+			pkt_num_unit = msb - MITI_COUNT_BIT_NUM;
+			pkt_num_count = pkt_num >> (msb - MITI_COUNT_BIT_NUM);
+		} else {
+			pkt_num_unit = 0;
+			pkt_num_count = pkt_num;
+		}
+	}
+
+	int_miti = u16_encode_bits(pkt_num_count, MITI_PKT_NUM_COUNT_MASK) |
+		   u16_encode_bits(pkt_num_unit, MITI_PKT_NUM_UNIT_MASK);
+
+	return int_miti;
+}
+
+static void rtase_init_software_variable(struct pci_dev *pdev,
+					 struct rtase_private *tp)
+{
+	u16 int_miti;
+
+	tp->tx_queue_ctrl = RTASE_TXQ_CTRL;
+	tp->func_tx_queue_num = RTASE_FUNC_TXQ_NUM;
+	tp->func_rx_queue_num = RTASE_FUNC_RXQ_NUM;
+	tp->int_nums = RTASE_INTERRUPT_NUM;
+
+	int_miti = rtase_calc_time_mitigation(MITI_DEFAULT_TIME) |
+		   rtase_calc_packet_num_mitigation(MITI_DEFAULT_PKT_NUM);
+	tp->tx_int_mit = int_miti;
+	tp->rx_int_mit = int_miti;
+
+	tp->sw_flag = 0;
+
+	rtase_init_int_vector(tp);
+
+	/* MTU range: 60 - hw-specific max */
+	tp->dev->min_mtu = ETH_ZLEN;
+	tp->dev->max_mtu = MAX_JUMBO_SIZE;
+}
+
+static bool rtase_check_mac_version_valid(struct rtase_private *tp)
+{
+	u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & HW_VER_MASK;
+	bool known_ver = false;
+
+	switch (hw_ver) {
+	case 0x00800000:
+	case 0x04000000:
+	case 0x04800000:
+		known_ver = true;
+		break;
+	}
+
+	return known_ver;
+}
+
+static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out,
+			    void __iomem **ioaddr_out)
+{
+	struct net_device *dev;
+	void __iomem *ioaddr;
+	int ret = -ENOMEM;
+
+	/* dev zeroed in alloc_etherdev */
+	dev = alloc_etherdev_mq(sizeof(struct rtase_private),
+				RTASE_FUNC_TXQ_NUM);
+	if (!dev)
+		goto err_out;
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_out_free_dev;
+
+	/* make sure PCI base addr 1 is MMIO */
+	if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+
+	/* check for weird/broken PCI region reporting */
+	if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+
+	ret = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (ret < 0)
+		goto err_out_disable;
+
+	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
+		dev->features |= NETIF_F_HIGHDMA;
+	else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+		goto err_out_free_res;
+	else
+		dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
+
+	pci_set_master(pdev);
+
+	/* ioremap MMIO region */
+	ioaddr = ioremap(pci_resource_start(pdev, 2),
+			 pci_resource_len(pdev, 2));
+	if (!ioaddr) {
+		ret = -EIO;
+		goto err_out_free_res;
+	}
+
+	*ioaddr_out = ioaddr;
+	*dev_out = dev;
+	goto out;
+
+err_out_free_res:
+	pci_release_regions(pdev);
+
+err_out_disable:
+	pci_disable_device(pdev);
+
+err_out_free_dev:
+	free_netdev(dev);
+
+err_out:
+	*ioaddr_out = NULL;
+	*dev_out = NULL;
+
+out:
+	return ret;
+}
+
+static void rtase_release_board(struct pci_dev *pdev, struct net_device *dev,
+				void __iomem *ioaddr)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	rtase_rar_set(tp, tp->org_mac_addr);
+	iounmap(ioaddr);
+
+	if ((tp->sw_flag & SWF_MSIX_ENABLED))
+		pci_disable_msix(pdev);
+	else
+		pci_disable_msi(pdev);
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	free_netdev(dev);
+}
+
+static int rtase_init_one(struct pci_dev *pdev,
+			  const struct pci_device_id *ent)
+{
+	struct net_device *dev = NULL;
+	void __iomem *ioaddr = NULL;
+	struct rtase_private *tp;
+	int ret;
+
+	if (!pdev->is_physfn && pdev->is_virtfn) {
+		dev_err(&pdev->dev, "This module does not support a virtual function.");
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "Automotive Switch Ethernet driver loaded\n");
+
+	ret = rtase_init_board(pdev, &dev, &ioaddr);
+	if (ret != 0)
+		return ret;
+
+	tp = netdev_priv(dev);
+	tp->mmio_addr = ioaddr;
+	tp->dev = dev;
+	tp->pdev = pdev;
+
+	/* identify chip attached to board */
+	if (!rtase_check_mac_version_valid(tp)) {
+		return dev_err_probe(&pdev->dev, -ENODEV,
+				     "unknown chip version, contact rtase maintainers (see MAINTAINERS file)\n");
+	}
+
+	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+	if (!dev->tstats)
+		goto err_out_1;
+
+	rtase_init_software_variable(pdev, tp);
+	rtase_init_hardware(tp);
+
+	ret = rtase_alloc_interrupt(pdev, tp);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to alloc MSIX/MSI\n");
+		goto err_out_1;
+	}
+
+	rtase_init_netdev_ops(dev);
+
+	dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+	dev->features |= NETIF_F_IP_CSUM;
+	dev->features |= NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+	dev->features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+	dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+			   NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX |
+			   NETIF_F_HW_VLAN_CTAG_RX;
+	dev->hw_features |= NETIF_F_RXALL;
+	dev->hw_features |= NETIF_F_RXFCS;
+	dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+	dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+			     NETIF_F_HIGHDMA;
+	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+	netif_set_tso_max_size(dev, LSO_64K);
+	netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
+
+	rtase_get_mac_address(dev);
+
+	tp->tally_vaddr = dma_alloc_coherent(&pdev->dev,
+					     sizeof(*tp->tally_vaddr),
+					     &tp->tally_paddr,
+					     GFP_KERNEL);
+	if (!tp->tally_vaddr) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	rtase_tally_counter_clear(tp);
+
+	pci_set_drvdata(pdev, dev);
+
+	ret = register_netdev(dev);
+	if (ret != 0)
+		goto err_out;
+
+	netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq);
+
+	netif_carrier_off(dev);
+
+	goto out;
+
+err_out:
+	if (tp->tally_vaddr) {
+		dma_free_coherent(&pdev->dev,
+				  sizeof(*tp->tally_vaddr),
+				  tp->tally_vaddr,
+				  tp->tally_paddr);
+
+		tp->tally_vaddr = NULL;
+	}
+
+err_out_1:
+	rtase_release_board(pdev, dev, ioaddr);
+
+out:
+	return ret;
+}
+
+static void rtase_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec;
+	u32 i;
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		netif_napi_del(&ivec->napi);
+	}
+
+	unregister_netdev(dev);
+	rtase_reset_interrupt(pdev, tp);
+	if (tp->tally_vaddr) {
+		dma_free_coherent(&pdev->dev,
+				  sizeof(*tp->tally_vaddr),
+				  tp->tally_vaddr,
+				  tp->tally_paddr);
+		tp->tally_vaddr = NULL;
+	}
+
+	rtase_release_board(pdev, dev, tp->mmio_addr);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static void rtase_shutdown(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	if (netif_running(dev))
+		rtase_close(dev);
+
+	rtase_reset_interrupt(pdev, tp);
+}
+
+#ifdef CONFIG_PM
+static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	if (!netif_running(dev))
+		goto out;
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+	netif_device_detach(dev);
+	rtase_hw_reset(dev);
+
+out:
+	pci_save_state(pdev);
+
+	return 0;
+}
+
+static int rtase_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	pci_enable_wake(pdev, PCI_D0, 0);
+
+	/* restore last modified mac address */
+	rtase_rar_set(tp, dev->dev_addr);
+
+	if (!netif_running(dev))
+		goto out;
+
+	rtase_wait_for_quiescence(dev);
+
+	rtase_tx_clear(tp);
+	rtase_rx_clear(tp);
+
+	ret = rtase_init_ring(dev);
+	if (ret) {
+		netdev_err(dev, "unable to init ring\n");
+		rtase_free_desc(tp);
+		return -ENOMEM;
+	}
+
+	rtase_hw_config(dev);
+	/* always link, so start to transmit & receive */
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+	netif_device_attach(dev);
+
+out:
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver rtase_pci_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = rtase_pci_tbl,
+	.probe = rtase_init_one,
+	.remove = rtase_remove_one,
+	.shutdown = rtase_shutdown,
+#ifdef CONFIG_PM
+	.suspend = rtase_suspend,
+	.resume = rtase_resume,
+#endif
+};
+
+module_pci_driver(rtase_pci_driver);
diff --git a/drivers/net/ethernet/realtek/rtase/tt.h b/drivers/net/ethernet/realtek/rtase/tt.h
new file mode 100644
index 000000000000..9239c518c504
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/tt.h
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ *  rtase is the Linux device driver released for Realtek Automotive Switch
+ *  controllers with PCI-Express interface.
+ *
+ *  Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef _RTASE_H_
+#define _RTASE_H_
+
+/* the low 32 bit address of receive buffer must be 8-byte alignment. */
+#define RTK_RX_ALIGN 8
+
+#define HW_VER_MASK 0x7C800000
+
+#define RX_DMA_BURST_256       4
+#define TX_DMA_BURST_UNLIMITED 7
+#define RX_BUF_SIZE            (PAGE_SIZE - \
+				SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+#define MAX_JUMBO_SIZE         (RX_BUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN)
+
+/* 3 means InterFrameGap = the shortest one */
+#define INTERFRAMEGAP 0x03
+
+#define RTASE_REGS_SIZE     256
+#define RTASE_PCI_REGS_SIZE 0x100
+
+#define MULTICAST_FILTER_MASK  GENMASK(30, 26)
+#define MULTICAST_FILTER_LIMIT 32
+
+#define RTASE_VLAN_FILTER_ENTRY_NUM 32
+#define RTASE_NUM_TX_QUEUE 8
+#define RTASE_NUM_RX_QUEUE 4
+
+#define RTASE_TXQ_CTRL      1
+#define RTASE_FUNC_TXQ_NUM  1
+#define RTASE_FUNC_RXQ_NUM  1
+#define RTASE_INTERRUPT_NUM 1
+
+#define MITI_TIME_COUNT_MASK     GENMASK(3, 0)
+#define MITI_TIME_UNIT_MASK      GENMASK(7, 4)
+#define MITI_DEFAULT_TIME        128
+#define MITI_MAX_TIME            491520
+#define MITI_PKT_NUM_COUNT_MASK  GENMASK(11, 8)
+#define MITI_PKT_NUM_UNIT_MASK   GENMASK(13, 12)
+#define MITI_DEFAULT_PKT_NUM     64
+#define MITI_MAX_PKT_NUM_IDX     3
+#define MITI_MAX_PKT_NUM_UNIT    16
+#define MITI_MAX_PKT_NUM         240
+#define MITI_COUNT_BIT_NUM       4
+
+#define RTASE_NUM_MSIX 4
+
+#define RTASE_DWORD_MOD 16
+
+/*****************************************************************************/
+enum rtase_registers {
+	RTASE_MAC0   = 0x0000,
+	RTASE_MAC4   = 0x0004,
+	RTASE_MAR0   = 0x0008,
+	RTASE_MAR1   = 0x000C,
+	RTASE_DTCCR0 = 0x0010,
+	RTASE_DTCCR4 = 0x0014,
+#define COUNTER_RESET BIT(0)
+#define COUNTER_DUMP  BIT(3)
+
+	RTASE_FCR    = 0x0018,
+#define FCR_RXQ_MASK    GENMASK(5, 4)
+#define FCR_VLAN_FTR_EN BIT(1)
+
+	RTASE_LBK_CTRL = 0x001A,
+#define LBK_ATLD BIT(1)
+#define LBK_CLR  BIT(0)
+
+	RTASE_TX_DESC_ADDR0   = 0x0020,
+	RTASE_TX_DESC_ADDR4   = 0x0024,
+	RTASE_TX_DESC_COMMAND = 0x0028,
+#define TX_DESC_CMD_CS BIT(15)
+#define TX_DESC_CMD_WE BIT(14)
+
+	RTASE_BOOT_CTL  = 0x6004,
+	RTASE_CLKSW_SET = 0x6018,
+
+	RTASE_CHIP_CMD = 0x0037,
+#define STOP_REQ      BIT(7)
+#define STOP_REQ_DONE BIT(6)
+#define RE            BIT(3)
+#define TE            BIT(2)
+
+	RTASE_IMR0 = 0x0038,
+	RTASE_ISR0 = 0x003C,
+#define TOK7 BIT(30)
+#define TOK6 BIT(28)
+#define TOK5 BIT(26)
+#define TOK4 BIT(24)
+#define FOVW BIT(6)
+#define RDU  BIT(4)
+#define TOK  BIT(2)
+#define ROK  BIT(0)
+
+	RTASE_IMR1 = 0x0800,
+	RTASE_ISR1 = 0x0802,
+#define Q_TOK BIT(4)
+#define Q_RDU BIT(1)
+#define Q_ROK BIT(0)
+
+	RTASE_EPHY_ISR = 0x6014,
+	RTASE_EPHY_IMR = 0x6016,
+
+	RTASE_TX_CONFIG_0 = 0x0040,
+#define TX_INTER_FRAME_GAP_MASK GENMASK(25, 24)
+	/* DMA burst value (0-7) is shift this many bits */
+#define TX_DMA_MASK             GENMASK(10, 8)
+
+	RTASE_RX_CONFIG_0 = 0x0044,
+#define RX_SINGLE_FETCH  BIT(14)
+#define RX_SINGLE_TAG    BIT(13)
+#define RX_MX_DMA_MASK   GENMASK(10, 8)
+#define ACPT_FLOW        BIT(7)
+#define ACCEPT_ERR       BIT(5)
+#define ACCEPT_RUNT      BIT(4)
+#define ACCEPT_BROADCAST BIT(3)
+#define ACCEPT_MULTICAST BIT(2)
+#define ACCEPT_MYPHYS    BIT(1)
+#define ACCEPT_ALLPHYS   BIT(0)
+#define ACCEPT_MASK      (ACPT_FLOW | ACCEPT_ERR | ACCEPT_RUNT | \
+			  ACCEPT_BROADCAST | ACCEPT_MULTICAST | \
+			  ACCEPT_MYPHYS | ACCEPT_ALLPHYS)
+
+	RTASE_RX_CONFIG_1 = 0x0046,
+#define RX_MAX_FETCH_DESC_MASK  GENMASK(15, 11)
+#define RX_NEW_DESC_FORMAT_EN   BIT(8)
+#define OUTER_VLAN_DETAG_EN     BIT(7)
+#define INNER_VLAN_DETAG_EN     BIT(6)
+#define PCIE_NEW_FLOW           BIT(2)
+#define PCIE_RELOAD_En          BIT(0)
+
+	RTASE_EEM = 0x0050,
+#define EEM_UNLOCK 0xC0
+
+	RTASE_TDFNR  = 0x0057,
+	RTASE_TPPOLL = 0x0090,
+	RTASE_PDR    = 0x00B0,
+	RTASE_FIFOR  = 0x00D3,
+#define TX_FIFO_EMPTY BIT(5)
+#define RX_FIFO_EMPTY BIT(4)
+
+	RTASE_PCPR = 0x00D8,
+#define PCPR_VLAN_FTR_EN BIT(6)
+
+	RTASE_RMS       = 0x00DA,
+	RTASE_CPLUS_CMD = 0x00E0,
+#define FORCE_RXFLOW_EN BIT(11)
+#define FORCE_TXFLOW_EN BIT(10)
+#define RX_CHKSUM       BIT(5)
+
+	RTASE_Q0_RX_DESC_ADDR0 = 0x00E4,
+	RTASE_Q0_RX_DESC_ADDR4 = 0x00E8,
+	RTASE_Q1_RX_DESC_ADDR0 = 0x4000,
+	RTASE_Q1_RX_DESC_ADDR4 = 0x4004,
+	RTASE_MTPS             = 0x00EC,
+#define TAG_NUM_SEL_MASK  GENMASK(10, 8)
+
+	RTASE_MISC = 0x00F2,
+#define RX_DV_GATE_EN BIT(3)
+
+	RTASE_TFUN_CTRL = 0x0400,
+#define TX_NEW_DESC_FORMAT_EN BIT(0)
+
+	RTASE_TX_CONFIG_1 = 0x203E,
+#define TC_MODE_MASK  GENMASK(11, 10)
+
+	RTASE_TOKSEL      = 0x2046,
+	RTASE_TXQCRDT_0   = 0x2500,
+	RTASE_RFIFONFULL  = 0x4406,
+	RTASE_INT_MITI_TX = 0x0A00,
+	RTASE_INT_MITI_RX = 0x0A80,
+
+	RTASE_VLAN_ENTRY_MEM_0 = 0x7234,
+	RTASE_VLAN_ENTRY_0     = 0xAC80,
+};
+
+enum desc_status_bit {
+	DESC_OWN = BIT(31), /* Descriptor is owned by NIC */
+	RING_END = BIT(30), /* End of descriptor ring */
+};
+
+enum sw_flag_content {
+	SWF_MSI_ENABLED  = BIT(1),
+	SWF_MSIX_ENABLED = BIT(2),
+};
+
+#define RSVD_MASK 0x3FFFC000
+
+struct tx_desc {
+	__le32 opts1;
+	__le32 opts2;
+	__le64 addr;
+	__le32 opts3;
+	__le32 reserved1;
+	__le32 reserved2;
+	__le32 reserved3;
+} __packed;
+
+/*------ offset 0 of tx descriptor ------*/
+#define TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */
+#define TX_LAST_FRAG  BIT(28) /* Tx Final segment of a packet */
+#define GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */
+#define GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */
+#define TX_VLAN_TAG   BIT(17) /* Add VLAN tag */
+
+/*------ offset 4 of tx descriptor ------*/
+#define TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */
+#define TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */
+#define TX_IPCS_C  BIT(29) /* Calculate IP checksum */
+#define TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */
+
+union rx_desc {
+	struct {
+		__le64 header_buf_addr;
+		__le32 reserved1;
+		__le32 opts_header_len;
+		__le64 addr;
+		__le32 reserved2;
+		__le32 opts1;
+	} __packed desc_cmd;
+
+	struct {
+		__le32 reserved1;
+		__le32 reserved2;
+		__le32 rss;
+		__le32 opts4;
+		__le32 reserved3;
+		__le32 opts3;
+		__le32 opts2;
+		__le32 opts1;
+	} __packed desc_status;
+} __packed;
+
+/*------ offset 28 of rx descriptor ------*/
+#define RX_FIRST_FRAG    BIT(25) /* Rx First segment of a packet */
+#define RX_LAST_FRAG     BIT(24) /* Rx Final segment of a packet */
+#define RX_RES           BIT(20)
+#define RX_RUNT          BIT(19)
+#define RX_RWT           BIT(18)
+#define RX_CRC           BIT(16)
+#define RX_V6F           BIT(31)
+#define RX_V4F           BIT(30)
+#define RX_UDPT          BIT(29)
+#define RX_TCPT          BIT(28)
+#define RX_IPF           BIT(26) /* IP checksum failed */
+#define RX_UDPF          BIT(25) /* UDP/IP checksum failed */
+#define RX_TCPF          BIT(24) /* TCP/IP checksum failed */
+#define RX_LBK_FIFO_FULL BIT(17) /* Loopback FIFO Full */
+#define RX_VLAN_TAG      BIT(16) /* VLAN tag available */
+
+#define NUM_DESC                1024
+#define RTASE_TX_RING_DESC_SIZE (NUM_DESC * sizeof(struct tx_desc))
+#define RTASE_RX_RING_DESC_SIZE (NUM_DESC * sizeof(union rx_desc))
+#define VLAN_ENTRY_CAREBIT      0xF0000000
+#define VLAN_TAG_MASK           GENMASK(15, 0)
+#define RX_PKT_SIZE_MASK        GENMASK(13, 0)
+
+/* txqos hardware definitions */
+#define RTASE_1T_CLOCK            64
+#define RTASE_1T_POWER            10000000
+#define RTASE_IDLESLOPE_INT_SHIFT 25
+#define RTASE_IDLESLOPE_INT_MASK  GENMASK(31, 25)
+
+#define IVEC_NAME_SIZE (IFNAMSIZ + 10)
+
+struct rtase_int_vector {
+	struct rtase_private *tp;
+	unsigned int irq;
+	u8 status;
+	char name[IVEC_NAME_SIZE];
+	u16 index;
+	u16 imr_addr;
+	u16 isr_addr;
+	u32 imr;
+	struct list_head ring_list;
+	struct napi_struct napi;
+	int (*poll)(struct napi_struct *napi, int budget);
+};
+
+struct rtase_ring {
+	struct rtase_int_vector *ivec;
+	void *desc;
+	dma_addr_t phy_addr;
+	u32 cur_idx;
+	u32 dirty_idx;
+	u16 index;
+
+	struct sk_buff *skbuff[NUM_DESC];
+	union {
+		u32 len[NUM_DESC];
+		dma_addr_t data_phy_addr[NUM_DESC];
+	} mis;
+
+	struct list_head ring_entry;
+	int (*ring_handler)(struct rtase_ring *ring, int budget);
+};
+
+struct rtase_txqos {
+	int hicredit;
+	int locredit;
+	int idleslope;
+	int sendslope;
+};
+
+struct rtase_private {
+	void __iomem *mmio_addr;
+	u32 sw_flag;
+	u32 mc_filter[2];
+
+	struct pci_dev *pdev;
+	struct net_device *dev;
+	u32 rx_buf_sz;
+
+	struct page_pool *page_pool;
+	struct rtase_ring tx_ring[RTASE_NUM_TX_QUEUE];
+	struct rtase_txqos tx_qos[RTASE_NUM_TX_QUEUE];
+	struct rtase_ring rx_ring[RTASE_NUM_RX_QUEUE];
+	struct rtase_counters *tally_vaddr;
+	dma_addr_t tally_paddr;
+
+	u32 vlan_filter_ctrl;
+	u16 vlan_filter_vid[RTASE_VLAN_FILTER_ENTRY_NUM];
+
+	struct delayed_work task;
+	u8 org_mac_addr[ETH_ALEN];
+	struct msix_entry msix_entry[RTASE_NUM_MSIX];
+	struct rtase_int_vector int_vector[RTASE_NUM_MSIX];
+
+	u16 tx_queue_ctrl;
+	u16 func_tx_queue_num;
+	u16 func_rx_queue_num;
+	u16 int_nums;
+	u16 tx_int_mit;
+	u16 rx_int_mit;
+};
+
+#define LSO_64K 64000
+
+#define NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4)
+
+#define TCPHO_MASK GENMASK(24, 18)
+
+#define MSS_MAX  0x07FF /* MSS value */
+#define MSS_MASK GENMASK(28, 18)
+
+#endif /* _RTASE_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 06/13] net:ethernet:realtek:rtase: Implement .ndo_start_xmit function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (4 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 07/13] net:ethernet:realtek:rtase: Implement a function to receive packets Justin Lai
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement .ndo_start_xmit function to fill the information of the packet
to be transmitted into the tx descriptor, and then the hardware will
transmit the packet using the information in the tx descriptor.
In addition, we also implemented the tx_handler function to enable the
tx descriptor to be reused.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 288 ++++++++++++++++++
 1 file changed, 288 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 4e04189050cc..c90941d5b989 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -252,6 +252,68 @@ static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
 		   cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
 }
 
+static bool rtase_tx_avail(struct rtase_ring *ring)
+{
+	u32 avail_num = READ_ONCE(ring->dirty_idx) + NUM_DESC -
+			READ_ONCE(ring->cur_idx);
+
+	return avail_num > MAX_SKB_FRAGS;
+}
+
+static int tx_handler(struct rtase_ring *ring, int budget)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	struct net_device *dev = tp->dev;
+	int workdone = 0;
+	u32 dirty_tx;
+	u32 tx_left;
+
+	dirty_tx = ring->dirty_idx;
+	tx_left = READ_ONCE(ring->cur_idx) - dirty_tx;
+
+	while (tx_left > 0) {
+		u32 entry = dirty_tx % NUM_DESC;
+		struct tx_desc *desc = ring->desc +
+				       sizeof(struct tx_desc) * entry;
+		u32 len = ring->mis.len[entry];
+		u32 status;
+
+		status = le32_to_cpu(desc->opts1);
+
+		if (status & DESC_OWN)
+			break;
+
+		rtase_unmap_tx_skb(tp->pdev, len, desc);
+		ring->mis.len[entry] = 0;
+		if (ring->skbuff[entry]) {
+			dev_consume_skb_any(ring->skbuff[entry]);
+			ring->skbuff[entry] = NULL;
+		}
+
+		dev->stats.tx_bytes += len;
+		dev->stats.tx_packets++;
+		dirty_tx++;
+		tx_left--;
+		workdone++;
+
+		if (workdone == budget)
+			break;
+	}
+
+	if (ring->dirty_idx != dirty_tx) {
+		WRITE_ONCE(ring->dirty_idx, dirty_tx);
+
+		if (__netif_subqueue_stopped(dev, ring->index) &&
+		    rtase_tx_avail(ring))
+			netif_start_subqueue(dev, ring->index);
+
+		if (ring->cur_idx != dirty_tx)
+			rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+	}
+
+	return workdone;
+}
+
 static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
 {
 	struct rtase_ring *ring = &tp->tx_ring[idx];
@@ -1007,6 +1069,231 @@ static int rtase_close(struct net_device *dev)
 	return 0;
 }
 
+static u32 rtase_tx_vlan_tag(const struct rtase_private *tp,
+			     const struct sk_buff *skb)
+{
+	return (skb_vlan_tag_present(skb)) ?
+		(TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00;
+}
+
+static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
+{
+	u8 ip_protocol;
+	u32 csum_cmd;
+
+	switch (vlan_get_protocol(skb)) {
+	case htons(ETH_P_IP):
+		csum_cmd = TX_IPCS_C;
+		ip_protocol = ip_hdr(skb)->protocol;
+		break;
+
+	case htons(ETH_P_IPV6):
+		csum_cmd = TX_IPV6F_C;
+		ip_protocol = ipv6_hdr(skb)->nexthdr;
+		break;
+
+	default:
+		ip_protocol = IPPROTO_RAW;
+		break;
+	}
+
+	if (ip_protocol == IPPROTO_TCP)
+		csum_cmd |= TX_TCPCS_C;
+	else if (ip_protocol == IPPROTO_UDP)
+		csum_cmd |= TX_UDPCS_C;
+	else
+		WARN_ON_ONCE(1);
+
+	csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+
+	return csum_cmd;
+}
+
+static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
+			    u32 opts1, u32 opts2)
+{
+	const struct skb_shared_info *info = skb_shinfo(skb);
+	const struct rtase_private *tp = ring->ivec->tp;
+	const u8 nr_frags = info->nr_frags;
+	struct tx_desc *txd = NULL;
+	u32 cur_frag, entry;
+	u64 pkt_len_cnt = 0;
+
+	entry = ring->cur_idx;
+	for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
+		const skb_frag_t *frag = &info->frags[cur_frag];
+		dma_addr_t mapping;
+		u32 status, len;
+		void *addr;
+
+		entry = (entry + 1) % NUM_DESC;
+
+		txd = ring->desc + sizeof(struct tx_desc) * entry;
+		len = skb_frag_size(frag);
+		addr = skb_frag_address(frag);
+		mapping = dma_map_single(&tp->pdev->dev, addr, len,
+					 DMA_TO_DEVICE);
+
+		if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+			if (unlikely(net_ratelimit()))
+				netdev_err(tp->dev,
+					   "Failed to map TX fragments DMA!\n");
+
+			goto err_out;
+		}
+
+		if (((entry + 1) % NUM_DESC) == 0)
+			status = (opts1 | len | RING_END);
+		else
+			status = opts1 | len;
+
+		if (cur_frag == (nr_frags - 1)) {
+			ring->skbuff[entry] = skb;
+			status |= TX_LAST_FRAG;
+		}
+
+		ring->mis.len[entry] = len;
+		txd->addr = cpu_to_le64(mapping);
+		txd->opts2 = cpu_to_le32(opts2);
+
+		/* make sure the operating fields have been updated */
+		wmb();
+		txd->opts1 = cpu_to_le32(status);
+		pkt_len_cnt += len;
+	}
+
+	return cur_frag;
+
+err_out:
+	rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
+	return -EIO;
+}
+
+static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+				    struct net_device *dev)
+{
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	struct rtase_private *tp = netdev_priv(dev);
+	u32 q_idx, entry, len, opts1, opts2;
+	u32 mss = shinfo->gso_size;
+	struct rtase_ring *ring;
+	struct tx_desc *txd;
+	dma_addr_t mapping;
+	bool stop_queue;
+	int frags;
+
+	/* multiqueues */
+	q_idx = skb_get_queue_mapping(skb);
+	ring = &tp->tx_ring[q_idx];
+
+	if (unlikely(!rtase_tx_avail(ring))) {
+		if (net_ratelimit())
+			netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+		goto err_stop;
+	}
+
+	entry = ring->cur_idx % NUM_DESC;
+	txd = ring->desc + sizeof(struct tx_desc) * entry;
+
+	opts1 = DESC_OWN;
+	opts2 = rtase_tx_vlan_tag(tp, skb);
+
+	/* tcp segmentation offload (or tcp large send) */
+	if (mss) {
+		if (shinfo->gso_type & SKB_GSO_TCPV4) {
+			opts1 |= GIANT_SEND_V4;
+		} else if (shinfo->gso_type & SKB_GSO_TCPV6) {
+			if (skb_cow_head(skb, 0))
+				goto err_dma_0;
+
+			tcp_v6_gso_csum_prep(skb);
+			opts1 |= GIANT_SEND_V6;
+		} else {
+			WARN_ON_ONCE(1);
+		}
+
+		opts1 |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+		opts2 |= u32_encode_bits(mss, MSS_MASK);
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		opts2 |= rtase_tx_csum(skb, dev);
+	}
+
+	frags = rtase_xmit_frags(ring, skb, opts1, opts2);
+	if (unlikely(frags < 0))
+		goto err_dma_0;
+
+	if (frags) {
+		len = skb_headlen(skb);
+		opts1 |= TX_FIRST_FRAG;
+	} else {
+		len = skb->len;
+		ring->skbuff[entry] = skb;
+		opts1 |= TX_FIRST_FRAG | TX_LAST_FRAG;
+	}
+
+	if (((entry + 1) % NUM_DESC) == 0)
+		opts1 |= (len | RING_END);
+	else
+		opts1 |= len;
+
+	mapping = dma_map_single(&tp->pdev->dev, skb->data, len,
+				 DMA_TO_DEVICE);
+
+	if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+		if (unlikely(net_ratelimit()))
+			netdev_err(dev, "Failed to map TX DMA!\n");
+
+		goto err_dma_1;
+	}
+
+	ring->mis.len[entry] = len;
+	txd->addr = cpu_to_le64(mapping);
+	txd->opts2 = cpu_to_le32(opts2);
+	txd->opts1 = cpu_to_le32(opts1 & ~DESC_OWN);
+
+	/* make sure the operating fields have been updated */
+	wmb();
+
+	txd->opts1 = cpu_to_le32(opts1);
+
+	skb_tx_timestamp(skb);
+
+	/* tx needs to see descriptor changes before updated cur_idx */
+	smp_wmb();
+
+	WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1);
+
+	stop_queue = !rtase_tx_avail(ring);
+	if (unlikely(stop_queue))
+		netif_stop_subqueue(dev, q_idx);
+
+	/* set polling bit */
+	rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+
+	if (unlikely(stop_queue)) {
+		/* make sure cur_idx and dirty_idx have been updated */
+		smp_rmb();
+		if (rtase_tx_avail(ring))
+			netif_start_subqueue(dev, q_idx);
+	}
+
+	return NETDEV_TX_OK;
+
+err_dma_1:
+	ring->skbuff[entry] = NULL;
+	rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
+
+err_dma_0:
+	dev->stats.tx_dropped++;
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+
+err_stop:
+	netif_stop_queue(dev);
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_BUSY;
+}
+
 static void rtase_enable_eem_write(const struct rtase_private *tp)
 {
 	u8 val;
@@ -1058,6 +1345,7 @@ static void rtase_netpoll(struct net_device *dev)
 static const struct net_device_ops rtase_netdev_ops = {
 	.ndo_open = rtase_open,
 	.ndo_stop = rtase_close,
+	.ndo_start_xmit = rtase_start_xmit,
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	.ndo_poll_controller = rtase_netpoll,
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 07/13] net:ethernet:realtek:rtase: Implement a function to receive packets
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (5 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 06/13] net:ethernet:realtek:rtase: Implement .ndo_start_xmit function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 08/13] net:ethernet:realtek:rtase: Implement net_device_ops Justin Lai
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement rx_handler to read the information of the rx descriptor,
thereby checking the packet accordingly and storing the packet
in the socket buffer to complete the reception of the packet.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 148 ++++++++++++++++++
 1 file changed, 148 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index c90941d5b989..5c03d9abaa26 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -452,6 +452,154 @@ static void rtase_rx_ring_clear(struct rtase_ring *ring)
 	}
 }
 
+static int rtase_fragmented_frame(u32 status)
+{
+	return (status & (RX_FIRST_FRAG | RX_LAST_FRAG)) !=
+		(RX_FIRST_FRAG | RX_LAST_FRAG);
+}
+
+static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff *skb,
+			  const union rx_desc *desc)
+{
+	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+	/* rx csum offload */
+	if (((opts2 & RX_V4F) && !(opts2 & RX_IPF)) || (opts2 & RX_V6F)) {
+		if (((opts2 & RX_TCPT) && !(opts2 & RX_TCPF)) ||
+		    ((opts2 & RX_UDPT) && !(opts2 & RX_UDPF))) {
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		} else {
+			skb->ip_summed = CHECKSUM_NONE;
+		}
+	} else {
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+}
+
+static void rtase_rx_vlan_skb(union rx_desc *desc, struct sk_buff *skb)
+{
+	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+	if (!(opts2 & RX_VLAN_TAG))
+		return;
+
+	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & VLAN_TAG_MASK));
+}
+
+static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb)
+{
+	struct rtase_int_vector *ivec = ring->ivec;
+
+	napi_gro_receive(&ivec->napi, skb);
+}
+
+static int rx_handler(struct rtase_ring *ring, int budget)
+{
+	const struct rtase_private *tp = ring->ivec->tp;
+	u32 pkt_size, cur_rx, delta, entry, status;
+	struct net_device *dev = tp->dev;
+	union rx_desc *desc_base = ring->desc;
+	struct sk_buff *skb;
+	union rx_desc *desc;
+	int workdone = 0;
+
+	if (!ring->desc)
+		return workdone;
+
+	cur_rx = ring->cur_idx;
+	entry = cur_rx % NUM_DESC;
+	desc = &desc_base[entry];
+
+	do {
+		/* make sure discriptor has been updated */
+		rmb();
+		status = le32_to_cpu(desc->desc_status.opts1);
+
+		if (status & DESC_OWN)
+			break;
+
+		if (unlikely(status & RX_RES)) {
+			if (net_ratelimit())
+				netdev_warn(dev, "Rx ERROR. status = %08x\n",
+					    status);
+
+			dev->stats.rx_errors++;
+
+			if (status & (RX_RWT | RX_RUNT))
+				dev->stats.rx_length_errors++;
+
+			if (status & RX_CRC)
+				dev->stats.rx_crc_errors++;
+
+			if (dev->features & NETIF_F_RXALL)
+				goto process_pkt;
+
+			rtase_mark_to_asic(desc, tp->rx_buf_sz);
+			goto skip_process_pkt;
+		}
+
+process_pkt:
+		pkt_size = status & RX_PKT_SIZE_MASK;
+		if (likely(!(dev->features & NETIF_F_RXFCS)))
+			pkt_size -= ETH_FCS_LEN;
+
+		/* the driver does not support incoming fragmented
+		 * frames. they are seen as a symptom of over-mtu
+		 * sized frames
+		 */
+		if (unlikely(rtase_fragmented_frame(status))) {
+			dev->stats.rx_dropped++;
+			dev->stats.rx_length_errors++;
+			rtase_mark_to_asic(desc, tp->rx_buf_sz);
+			continue;
+		}
+
+		skb = ring->skbuff[entry];
+		dma_sync_single_for_cpu(&tp->pdev->dev,
+					ring->mis.data_phy_addr[entry],
+					tp->rx_buf_sz, DMA_FROM_DEVICE);
+
+		ring->skbuff[entry] = NULL;
+
+		if (dev->features & NETIF_F_RXCSUM)
+			rtase_rx_csum(tp, skb, desc);
+
+		skb->dev = dev;
+		skb_put(skb, pkt_size);
+		skb_mark_for_recycle(skb);
+		skb->protocol = eth_type_trans(skb, dev);
+
+		if (skb->pkt_type == PACKET_MULTICAST)
+			dev->stats.multicast++;
+
+		rtase_rx_vlan_skb(desc, skb);
+		rtase_rx_skb(ring, skb);
+
+		dev->stats.rx_bytes += pkt_size;
+		dev->stats.rx_packets++;
+
+skip_process_pkt:
+		workdone++;
+		cur_rx++;
+		entry = cur_rx % NUM_DESC;
+		desc = ring->desc + sizeof(union rx_desc) * entry;
+		prefetch(desc);
+	} while (workdone != budget);
+
+	ring->cur_idx = cur_rx;
+	delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx, 1);
+
+	if (!delta && workdone)
+		netdev_info(dev, "no Rx buffer allocated\n");
+
+	ring->dirty_idx += delta;
+
+	if ((ring->dirty_idx + NUM_DESC) == ring->cur_idx)
+		netdev_emerg(dev, "Rx buffers exhausted\n");
+
+	return workdone;
+}
+
 static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
 {
 	struct rtase_ring *ring = &tp->rx_ring[idx];
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 08/13] net:ethernet:realtek:rtase: Implement net_device_ops
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (6 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 07/13] net:ethernet:realtek:rtase: Implement a function to receive packets Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function Justin Lai
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

1. Implement .ndo_set_rx_mode so that the device can change address
list filtering.
2. Implement .ndo_set_mac_address so that mac address can be changed.
3. Implement .ndo_change_mtu so that mtu can be changed.
4. Implement .ndo_tx_timeout to perform related processing when the
transmitter does not make any progress.
5. Implement .ndo_get_stats64 to provide statistics that are called
when the user wants to get network device usage.
6. Implement .ndo_vlan_rx_add_vid to register VLAN ID when the device
supports VLAN filtering.
7. Implement .ndo_vlan_rx_kill_vid to unregister VLAN ID when the device
supports VLAN filtering.
8. Implement the .ndo_setup_tc to enable setting any "tc" scheduler,
classifier or action on dev.
9. Implement .ndo_fix_features enables adjusting requested feature flags
based on device-specific constraints.
10. Implement .ndo_set_features enables updating device configuration to
new features.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 378 ++++++++++++++++++
 1 file changed, 378 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 5c03d9abaa26..12607663dd72 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -1442,6 +1442,11 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
 	return NETDEV_TX_BUSY;
 }
 
+static void rtase_set_rx_mode(struct net_device *dev)
+{
+	rtase_hw_set_rx_packet_filter(dev);
+}
+
 static void rtase_enable_eem_write(const struct rtase_private *tp)
 {
 	u8 val;
@@ -1474,6 +1479,293 @@ static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
 	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
 }
 
+static int rtase_set_mac_address(struct net_device *dev, void *p)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	ret = eth_mac_addr(dev, p);
+	if (ret)
+		return ret;
+
+	rtase_rar_set(tp, dev->dev_addr);
+
+	return 0;
+}
+
+static int rtase_change_mtu(struct net_device *dev, int new_mtu)
+{
+	dev->mtu = new_mtu;
+
+	netdev_update_features(dev);
+
+	return 0;
+}
+
+static void rtase_wait_for_quiescence(const struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	struct rtase_int_vector *ivec;
+	u32 i;
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		synchronize_irq(ivec->irq);
+		/* wait for any pending NAPI task to complete */
+		napi_disable(&ivec->napi);
+	}
+
+	rtase_irq_dis_and_clear(tp);
+
+	for (i = 0; i < tp->int_nums; i++) {
+		ivec = &tp->int_vector[i];
+		napi_enable(&ivec->napi);
+	}
+}
+
+static void rtase_sw_reset(struct net_device *dev)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+	rtase_hw_reset(dev);
+
+	/* let's wait a bit while any (async) irq lands on */
+	rtase_wait_for_quiescence(dev);
+	rtase_tx_clear(tp);
+	rtase_rx_clear(tp);
+
+	ret = rtase_init_ring(dev);
+	if (ret) {
+		netdev_err(dev, "unable to init ring\n");
+		rtase_free_desc(tp);
+		return;
+	}
+
+	rtase_hw_config(dev);
+	/* always link, so start to transmit & receive */
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+}
+
+static void rtase_dump_tally_counter(const struct rtase_private *tp)
+{
+	dma_addr_t paddr = tp->tally_paddr;
+	u32 cmd = lower_32_bits(paddr);
+	u32 val;
+	int err;
+
+	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(paddr));
+	rtase_w32(tp, RTASE_DTCCR0, cmd);
+	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_DUMP);
+
+	err = read_poll_timeout(rtase_r32, val, !(val & COUNTER_DUMP), 10, 250,
+				false, tp, RTASE_DTCCR0);
+
+	if (err == -ETIMEDOUT)
+		netdev_err(tp->dev, "error occurred in dump tally counter\n");
+}
+
+static void rtase_dump_state(const struct net_device *dev)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters;
+	int max_reg_size = RTASE_PCI_REGS_SIZE;
+	const struct rtase_ring *ring;
+	u32 dword_rd;
+	int n = 0;
+
+	ring = &tp->tx_ring[0];
+	netdev_err(dev, "Tx descriptor info:\n");
+	netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
+	netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
+	netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
+
+	ring = &tp->rx_ring[0];
+	netdev_err(dev, "Rx descriptor info:\n");
+	netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
+	netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
+	netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
+
+	netdev_err(dev, "Device Registers:\n");
+	netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp, RTASE_CHIP_CMD));
+	netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
+	netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
+	netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
+		   rtase_r16(tp, RTASE_BOOT_CTL));
+	netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
+		   rtase_r16(tp, RTASE_EPHY_ISR));
+	netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
+		   rtase_r16(tp, RTASE_EPHY_IMR));
+	netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
+		   rtase_r16(tp, RTASE_CLKSW_SET));
+
+	netdev_err(dev, "Dump PCI Registers:\n");
+
+	while (n < max_reg_size) {
+		if ((n % RTASE_DWORD_MOD) == 0)
+			netdev_err(tp->dev, "0x%03x:\n", n);
+
+		pci_read_config_dword(tp->pdev, n, &dword_rd);
+		netdev_err(tp->dev, "%08x\n", dword_rd);
+		n += 4;
+	}
+
+	netdev_err(dev, "Dump tally counter:\n");
+	counters = tp->tally_vaddr;
+	rtase_dump_tally_counter(tp);
+
+	netdev_err(dev, "tx_packets %lld\n",
+		   le64_to_cpu(counters->tx_packets));
+	netdev_err(dev, "rx_packets %lld\n",
+		   le64_to_cpu(counters->rx_packets));
+	netdev_err(dev, "tx_errors %lld\n",
+		   le64_to_cpu(counters->tx_errors));
+	netdev_err(dev, "rx_missed %lld\n",
+		   le64_to_cpu(counters->rx_missed));
+	netdev_err(dev, "align_errors %lld\n",
+		   le64_to_cpu(counters->align_errors));
+	netdev_err(dev, "tx_one_collision %lld\n",
+		   le64_to_cpu(counters->tx_one_collision));
+	netdev_err(dev, "tx_multi_collision %lld\n",
+		   le64_to_cpu(counters->tx_multi_collision));
+	netdev_err(dev, "rx_unicast %lld\n",
+		   le64_to_cpu(counters->rx_unicast));
+	netdev_err(dev, "rx_broadcast %lld\n",
+		   le64_to_cpu(counters->rx_broadcast));
+	netdev_err(dev, "rx_multicast %lld\n",
+		   le64_to_cpu(counters->rx_multicast));
+	netdev_err(dev, "tx_aborted %lld\n",
+		   le64_to_cpu(counters->tx_aborted));
+	netdev_err(dev, "tx_underun %lld\n",
+		   le64_to_cpu(counters->tx_underun));
+}
+
+static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+	rtase_dump_state(dev);
+	rtase_sw_reset(dev);
+}
+
+static void rtase_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *stats)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters = tp->tally_vaddr;
+
+	if (!counters)
+		return;
+
+	netdev_stats_to_stats64(stats, &dev->stats);
+	dev_fetch_sw_netstats(stats, dev->tstats);
+
+	/* fetch additional counter values missing in stats collected by driver
+	 * from tally counter
+	 */
+	rtase_dump_tally_counter(tp);
+
+	stats->tx_errors = le64_to_cpu(counters->tx_errors);
+	stats->collisions = le32_to_cpu(counters->tx_multi_collision);
+	stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted);
+	stats->rx_missed_errors = le16_to_cpu(counters->rx_missed);
+}
+
+static void rtase_enable_vlan_filter(const struct rtase_private *tp, bool enabled)
+{
+	u16 tmp;
+
+	if (enabled == 1) {
+		tmp = rtase_r16(tp, RTASE_FCR);
+		if (!(tmp & FCR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_FCR, tmp | FCR_VLAN_FTR_EN);
+
+		tmp = rtase_r16(tp, RTASE_PCPR);
+		if (!(tmp & PCPR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_PCPR, tmp | PCPR_VLAN_FTR_EN);
+	} else {
+		tmp = rtase_r16(tp, RTASE_FCR);
+		if (tmp & FCR_VLAN_FTR_EN)
+			rtase_w16(tp, RTASE_FCR, tmp & ~FCR_VLAN_FTR_EN);
+
+		tmp = rtase_r16(tp, RTASE_PCPR);
+		if (!(tmp & PCPR_VLAN_FTR_EN))
+			rtase_w16(tp, RTASE_PCPR, tmp & ~PCPR_VLAN_FTR_EN);
+	}
+}
+
+static int rtase_vlan_rx_add_vid(struct net_device *dev, __be16 protocol,
+				 u16 vid)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u16 tmp_mem, i;
+
+	if (be16_to_cpu(protocol) != ETH_P_8021Q)
+		return -EINVAL;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+		u16 addr, mask;
+
+		if (!(tp->vlan_filter_ctrl & BIT(i))) {
+			tp->vlan_filter_ctrl |= BIT(i);
+			tp->vlan_filter_vid[i] = vid;
+			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4,
+				  vid | VLAN_ENTRY_CAREBIT);
+			/* each 16-bit register contains two VLAN entries */
+			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+			mask = 0x1 << ((i & 0x1) * 8);
+			tmp_mem = rtase_r16(tp, addr);
+			tmp_mem |= mask;
+			rtase_w16(tp, addr, tmp_mem);
+			break;
+		}
+	}
+
+	if (i == RTASE_VLAN_FILTER_ENTRY_NUM)
+		return -ENOENT;
+
+	rtase_enable_vlan_filter(tp, true);
+
+	return 0;
+}
+
+static int rtase_vlan_rx_kill_vid(struct net_device *dev, __be16 protocol,
+				  u16 vid)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	u16 tmp_mem, i;
+
+	if (be16_to_cpu(protocol) != ETH_P_8021Q)
+		return -EINVAL;
+
+	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+		u16 addr, mask;
+
+		if (tp->vlan_filter_vid[i] == vid) {
+			tp->vlan_filter_ctrl &= ~BIT(i);
+			tp->vlan_filter_vid[i] = 0;
+			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
+
+			/* each 16-bit register contains two VLAN entries */
+			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+			mask = ~(0x1 << ((i & 0x1) * 8));
+			tmp_mem = rtase_r16(tp, addr);
+			tmp_mem &= mask;
+			rtase_w16(tp, addr, tmp_mem);
+			break;
+		}
+	}
+
+	/* check vlan filter enabled */
+	if (!tp->vlan_filter_ctrl)
+		rtase_enable_vlan_filter(tp, false);
+
+	return 0;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /* Polling 'interrupt' - used by things like netconsole to send skbs
  * without having to re-enable interrupts. It's not called while
@@ -1490,13 +1782,99 @@ static void rtase_netpoll(struct net_device *dev)
 }
 #endif
 
+static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue)
+{
+	u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK;
+	u32 val, i;
+
+	val = u32_encode_bits(idle / RTASE_1T_POWER, RTASE_IDLESLOPE_INT_MASK);
+	idle %= RTASE_1T_POWER;
+
+	for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) {
+		idle *= 2;
+		if ((idle / RTASE_1T_POWER) == 1)
+			val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i);
+
+		idle %= RTASE_1T_POWER;
+	}
+	rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val);
+}
+
+static void rtase_setup_tc_cbs(struct rtase_private *tp,
+			       const struct tc_cbs_qopt_offload *qopt)
+{
+	u32 queue = qopt->queue;
+
+	tp->tx_qos[queue].hicredit = qopt->hicredit;
+	tp->tx_qos[queue].locredit = qopt->locredit;
+	tp->tx_qos[queue].idleslope = qopt->idleslope;
+	tp->tx_qos[queue].sendslope = qopt->sendslope;
+
+	/* set hardware cbs */
+	rtase_set_hw_cbs(tp, queue);
+}
+
+static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type,
+			  void *type_data)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+
+	switch (type) {
+	case TC_SETUP_QDISC_CBS:
+		rtase_setup_tc_cbs(tp, type_data);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static netdev_features_t rtase_fix_features(struct net_device *dev,
+					    netdev_features_t features)
+{
+	netdev_features_t features_fix = features;
+
+	if (dev->mtu > MSS_MAX)
+		features_fix &= ~NETIF_F_ALL_TSO;
+
+	if (dev->mtu > ETH_DATA_LEN)
+		features_fix &= ~NETIF_F_ALL_TSO;
+
+	return features_fix;
+}
+
+static int rtase_set_features(struct net_device *dev,
+			      netdev_features_t features)
+{
+	netdev_features_t features_set = features;
+
+	features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM |
+			NETIF_F_HW_VLAN_CTAG_RX;
+
+	if (features_set ^ dev->features)
+		rtase_hw_set_features(dev, features_set);
+
+	return 0;
+}
+
 static const struct net_device_ops rtase_netdev_ops = {
 	.ndo_open = rtase_open,
 	.ndo_stop = rtase_close,
 	.ndo_start_xmit = rtase_start_xmit,
+	.ndo_set_rx_mode = rtase_set_rx_mode,
+	.ndo_set_mac_address = rtase_set_mac_address,
+	.ndo_change_mtu = rtase_change_mtu,
+	.ndo_tx_timeout = rtase_tx_timeout,
+	.ndo_get_stats64 = rtase_get_stats64,
+	.ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	.ndo_poll_controller = rtase_netpoll,
 #endif
+	.ndo_setup_tc = rtase_setup_tc,
+	.ndo_fix_features = rtase_fix_features,
+	.ndo_set_features = rtase_set_features,
 };
 
 static void rtase_get_mac_address(struct net_device *dev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (7 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 08/13] net:ethernet:realtek:rtase: Implement net_device_ops Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 15:02   ` Heiner Kallweit
  2023-11-15 13:34 ` [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function Justin Lai
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement the pci_driver suspend function to enable the device
to sleep, and implement the resume function to enable the device
to resume operation.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 12607663dd72..b7679b74cc8a 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -2323,12 +2323,76 @@ static void rtase_shutdown(struct pci_dev *pdev)
 	rtase_reset_interrupt(pdev, tp);
 }
 
+#ifdef CONFIG_PM
+static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	if (!netif_running(dev))
+		goto out;
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+	netif_device_detach(dev);
+	rtase_hw_reset(dev);
+
+out:
+	pci_save_state(pdev);
+
+	return 0;
+}
+
+static int rtase_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtase_private *tp = netdev_priv(dev);
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	pci_enable_wake(pdev, PCI_D0, 0);
+
+	/* restore last modified mac address */
+	rtase_rar_set(tp, dev->dev_addr);
+
+	if (!netif_running(dev))
+		goto out;
+
+	rtase_wait_for_quiescence(dev);
+
+	rtase_tx_clear(tp);
+	rtase_rx_clear(tp);
+
+	ret = rtase_init_ring(dev);
+	if (ret) {
+		netdev_err(dev, "unable to init ring\n");
+		rtase_free_desc(tp);
+		return -ENOMEM;
+	}
+
+	rtase_hw_config(dev);
+	/* always link, so start to transmit & receive */
+	rtase_hw_start(dev);
+
+	netif_carrier_on(dev);
+	netif_wake_queue(dev);
+	netif_device_attach(dev);
+
+out:
+	return 0;
+}
+#endif /* CONFIG_PM */
+
 static struct pci_driver rtase_pci_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = rtase_pci_tbl,
 	.probe = rtase_init_one,
 	.remove = rtase_remove_one,
 	.shutdown = rtase_shutdown,
+#ifdef CONFIG_PM
+	.suspend = rtase_suspend,
+	.resume = rtase_resume,
+#endif
 };
 
 module_pci_driver(rtase_pci_driver);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (8 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 15:02   ` Heiner Kallweit
  2023-11-15 13:34 ` [PATCH net-next v11 11/13] net:ethernet:realtek:rtase: Add a Makefile in the rtase folder Justin Lai
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Implement the ethtool function to support users to obtain network card
information, including obtaining various device settings, Report whether
physical link is up, Report pause parameters, Set pause parameters,
Return a set of strings that describe the requested objects, Get number
of strings that @get_strings will write, Return extended statistics
about the device.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index b7679b74cc8a..5ea4d51fcc47 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -1900,9 +1900,153 @@ static void rtase_get_mac_address(struct net_device *dev)
 	ether_addr_copy(dev->perm_addr, dev->dev_addr);
 }
 
+static void rtase_get_drvinfo(struct net_device *dev,
+			      struct ethtool_drvinfo *drvinfo)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
+	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
+}
+
+static int rtase_get_settings(struct net_device *dev,
+			      struct ethtool_link_ksettings *cmd)
+{
+	u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
+						supported);
+	cmd->base.speed = SPEED_5000;
+	cmd->base.duplex = DUPLEX_FULL;
+	cmd->base.port = PORT_MII;
+	cmd->base.autoneg = AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static void rtase_get_pauseparam(struct net_device *dev,
+				 struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	pause->autoneg = AUTONEG_DISABLE;
+
+	if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
+	    (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+		pause->tx_pause = 1;
+	} else if ((value & FORCE_TXFLOW_EN)) {
+		pause->tx_pause = 1;
+	} else if ((value & FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+	}
+}
+
+static int rtase_set_pauseparam(struct net_device *dev,
+				struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	if (pause->autoneg)
+		return -EOPNOTSUPP;
+
+	value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
+
+	if (pause->tx_pause)
+		value |= FORCE_TXFLOW_EN;
+
+	if (pause->rx_pause)
+		value |= FORCE_RXFLOW_EN;
+
+	rtase_w16(tp, RTASE_CPLUS_CMD, value);
+	return 0;
+}
+
+static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
+	"tx_packets",
+	"rx_packets",
+	"tx_errors",
+	"rx_errors",
+	"rx_missed",
+	"align_errors",
+	"tx_single_collisions",
+	"tx_multi_collisions",
+	"unicast",
+	"broadcast",
+	"multicast",
+	"tx_aborted",
+	"tx_underrun",
+};
+
+static void rtase_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
+		break;
+	}
+}
+
+static int rtase_get_sset_count(struct net_device *dev, int sset)
+{
+	int ret = -EOPNOTSUPP;
+
+	switch (sset) {
+	case ETH_SS_STATS:
+		ret = ARRAY_SIZE(rtase_gstrings);
+		break;
+	}
+
+	return ret;
+}
+
+static void rtase_get_ethtool_stats(struct net_device *dev,
+				    struct ethtool_stats *stats, u64 *data)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters;
+
+	ASSERT_RTNL();
+
+	counters = tp->tally_vaddr;
+	if (!counters)
+		return;
+
+	rtase_dump_tally_counter(tp);
+
+	data[0] = le64_to_cpu(counters->tx_packets);
+	data[1] = le64_to_cpu(counters->rx_packets);
+	data[2] = le64_to_cpu(counters->tx_errors);
+	data[3] = le32_to_cpu(counters->rx_errors);
+	data[4] = le16_to_cpu(counters->rx_missed);
+	data[5] = le16_to_cpu(counters->align_errors);
+	data[6] = le32_to_cpu(counters->tx_one_collision);
+	data[7] = le32_to_cpu(counters->tx_multi_collision);
+	data[8] = le64_to_cpu(counters->rx_unicast);
+	data[9] = le64_to_cpu(counters->rx_broadcast);
+	data[10] = le32_to_cpu(counters->rx_multicast);
+	data[11] = le16_to_cpu(counters->tx_aborted);
+	data[12] = le16_to_cpu(counters->tx_underun);
+}
+
+static const struct ethtool_ops rtase_ethtool_ops = {
+	.get_drvinfo = rtase_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+	.get_link_ksettings = rtase_get_settings,
+	.get_pauseparam = rtase_get_pauseparam,
+	.set_pauseparam = rtase_set_pauseparam,
+	.get_strings = rtase_get_strings,
+	.get_sset_count = rtase_get_sset_count,
+	.get_ethtool_stats = rtase_get_ethtool_stats,
+	.get_ts_info = ethtool_op_get_ts_info,
+};
+
 static void rtase_init_netdev_ops(struct net_device *dev)
 {
 	dev->netdev_ops = &rtase_netdev_ops;
+	dev->ethtool_ops = &rtase_ethtool_ops;
 }
 
 static void rtase_reset_interrupt(struct pci_dev *pdev,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 11/13] net:ethernet:realtek:rtase: Add a Makefile in the rtase folder
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (9 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 13:34 ` [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder Justin Lai
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Add a Makefile in the rtase folder to build rtase driver.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 drivers/net/ethernet/realtek/rtase/Makefile | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 drivers/net/ethernet/realtek/rtase/Makefile

diff --git a/drivers/net/ethernet/realtek/rtase/Makefile b/drivers/net/ethernet/realtek/rtase/Makefile
new file mode 100644
index 000000000000..f29b14687950
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+# Copyright(c) 2023 Realtek Semiconductor Corp. All rights reserved.
+
+#
+# Makefile for the Realtek PCIe driver
+#
+
+obj-$(CONFIG_RTASE) += rtase.o
+
+rtase-objs := rtase_main.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (10 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 11/13] net:ethernet:realtek:rtase: Add a Makefile in the rtase folder Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-15 19:58   ` kernel test robot
  2023-11-21 10:00   ` kernel test robot
  2023-11-15 13:34 ` [PATCH net-next v11 13/13] MAINTAINERS: Add the rtase ethernet driver entry Justin Lai
  2023-11-16 17:57 ` [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Simon Horman
  13 siblings, 2 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

1. Add the RTASE entry in the Kconfig.
2. Add the CONFIG_RTASE entry in the Makefile.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 drivers/net/ethernet/realtek/Kconfig  | 17 +++++++++++++++++
 drivers/net/ethernet/realtek/Makefile |  1 +
 2 files changed, 18 insertions(+)

diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 93d9df55b361..57ef924deebd 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -113,4 +113,21 @@ config R8169
 	  To compile this driver as a module, choose M here: the module
 	  will be called r8169.  This is recommended.
 
+config RTASE
+	tristate "Realtek Automotive Switch 9054/9068/9072/9075/9068/9071 PCIe Interface support"
+	depends on PCI
+	select CRC32
+	help
+	  Say Y here if you have a Realtek Ethernet adapter belonging to
+	  the following families:
+	  RTL9054 5GBit Ethernet
+	  RTL9068 5GBit Ethernet
+	  RTL9072 5GBit Ethernet
+	  RTL9075 5GBit Ethernet
+	  RTL9068 5GBit Ethernet
+	  RTL9071 5GBit Ethernet
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rtase. This is recommended.
+
 endif # NET_VENDOR_REALTEK
diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile
index 2e1d78b106b0..0c1c16f63e9a 100644
--- a/drivers/net/ethernet/realtek/Makefile
+++ b/drivers/net/ethernet/realtek/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_8139TOO) += 8139too.o
 obj-$(CONFIG_ATP) += atp.o
 r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
 obj-$(CONFIG_R8169) += r8169.o
+obj-$(CONFIG_RTASE) += rtase/
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH net-next v11 13/13] MAINTAINERS: Add the rtase ethernet driver entry
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (11 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder Justin Lai
@ 2023-11-15 13:34 ` Justin Lai
  2023-11-16 17:57 ` [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Simon Horman
  13 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-15 13:34 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu, Justin Lai

Add myself and Larry Chiu as the maintainer for the rtase ethernet driver.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 53b7ca804465..239aae94dc0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18476,6 +18476,13 @@ L:	linux-remoteproc@vger.kernel.org
 S:	Maintained
 F:	drivers/tty/rpmsg_tty.c
 
+RTASE ETHERNET DRIVER
+M:	Justin Lai <justinlai0215@realtek.com>
+M:	Larry Chiu <larry.chiu@realtek.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/ethernet/realtek/rtase/
+
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
@ 2023-11-15 15:02   ` Heiner Kallweit
  2023-11-21 11:54     ` Justin Lai
  2023-11-16 18:06   ` Simon Horman
  2023-11-18 22:50   ` Jakub Kicinski
  2 siblings, 1 reply; 28+ messages in thread
From: Heiner Kallweit @ 2023-11-15 15:02 UTC (permalink / raw)
  To: Justin Lai, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu

On 15.11.2023 14:34, Justin Lai wrote:
> Implement rtase_hw_config to set default hardware settings, including
> setting interrupt mitigation, tx/rx DMA burst, interframe gap time,
> rx packet filter, near fifo threshold and fill descriptor ring and
> tally counter address, and enable flow control. When filling the
> rx descriptor ring, the first group of queues needs to be processed
> separately because the positions of the first group of queues are not
> regular with other subsequent groups. The other queues are all newly
> added features, but we want to retain the original design. So they were
> not put together.
>
> Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> ---
>  .../net/ethernet/realtek/rtase/rtase_main.c   |  243 ++
>  drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
>  drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++
>  3 files changed, 3138 insertions(+)
>  create mode 100644 drivers/net/ethernet/realtek/rtase/tt.c
>  create mode 100644 drivers/net/ethernet/realtek/rtase/tt.h
>
> diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> index 46d128a68844..4e04189050cc 100644
> --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> @@ -466,6 +466,25 @@ static int rtase_init_ring(const struct net_device *dev)
>  	return -ENOMEM;
>  }
>
> +static void rtase_interrupt_mitigation(const struct rtase_private *tp)
> +{
> +	u32 i;
> +
> +	/* tx interrupt mitigation */
> +	for (i = 0; i < tp->func_tx_queue_num; i++)
> +		rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
> +
> +	/* rx interrupt mitigation */
> +	for (i = 0; i < tp->func_rx_queue_num; i++)
> +		rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
> +}
> +
> +static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
> +{
> +	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> +	rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
> +}
> +
>  static void rtase_tally_counter_clear(const struct rtase_private *tp)
>  {
>  	u32 cmd = lower_32_bits(tp->tally_paddr);
> @@ -474,6 +493,123 @@ static void rtase_tally_counter_clear(const struct rtase_private *tp)
>  	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
>  }
>
> +static void rtase_desc_addr_fill(const struct rtase_private *tp)
> +{
> +	const struct rtase_ring *ring;
> +	u16 i, cmd, val;
> +	int err;
> +
> +	for (i = 0; i < tp->func_tx_queue_num; i++) {
> +		ring = &tp->tx_ring[i];
> +
> +		rtase_w32(tp, RTASE_TX_DESC_ADDR0,
> +			  lower_32_bits(ring->phy_addr));
> +		rtase_w32(tp, RTASE_TX_DESC_ADDR4,
> +			  upper_32_bits(ring->phy_addr));
> +
> +		cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
> +		rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
> +
> +		err = read_poll_timeout(rtase_r16, val, !(val & TX_DESC_CMD_CS),
> +					10, 1000, false, tp, RTASE_TX_DESC_COMMAND);
> +
> +		if (err == -ETIMEDOUT)
> +			netdev_err(tp->dev, "error occurred in fill tx descriptor\n");
> +	}
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++) {
> +		ring = &tp->rx_ring[i];
> +
> +		if (i == 0) {
> +			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
> +				  lower_32_bits(ring->phy_addr));
> +			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
> +				  upper_32_bits(ring->phy_addr));
> +		} else {
> +			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i - 1) * 8)),
> +				  lower_32_bits(ring->phy_addr));
> +			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i - 1) * 8)),
> +				  upper_32_bits(ring->phy_addr));
> +		}
> +	}
> +}
> +
> +static void rtase_hw_set_features(const struct net_device *dev,
> +				  netdev_features_t features)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 rx_config, val;
> +
> +	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> +	if (features & NETIF_F_RXALL)
> +		rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
> +	else
> +		rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
> +
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> +
> +	val = rtase_r16(tp, RTASE_CPLUS_CMD);
> +	if (features & NETIF_F_RXCSUM)
> +		rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
> +	else
> +		rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
> +
> +	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
> +	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
> +		rx_config |= (INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
> +	else
> +		rx_config &= ~(INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
> +
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
> +}
> +
> +static void rtase_set_mar(const struct rtase_private *tp)
> +{
> +	rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
> +	rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
> +}
> +
> +static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
> +	u16 rx_mode;
> +
> +	rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
> +	rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
> +	} else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
> +		   (dev->flags & IFF_ALLMULTI)) {
> +		/* too many to filter perfectly -- accept all multicasts */

This comment seems to exist since RTL8139. The filtering seems to be
hash-based, so there's no perfect filtering anyway.
What's the benefit of the filter limit?

> +		rx_mode |= ACCEPT_MULTICAST;
> +	} else {
> +		struct netdev_hw_addr *hw_addr;
> +
> +		mc_filter[0] = 0;
> +		mc_filter[1] = 0;
> +
> +		netdev_for_each_mc_addr(hw_addr, dev) {
> +			u32 bit_nr = eth_hw_addr_crc(hw_addr);
> +			u32 idx = u32_get_bits(bit_nr, BIT(31));
> +			u32 bit = u32_get_bits(bit_nr, MULTICAST_FILTER_MASK);
> +
> +			mc_filter[idx] |= BIT(bit);
> +			rx_mode |= ACCEPT_MULTICAST;
> +		}
> +	}
> +
> +	if (dev->features & NETIF_F_RXALL)
> +		rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
> +
> +	tp->mc_filter[0] = swab32(mc_filter[1]);
> +	tp->mc_filter[1] = swab32(mc_filter[0]);
> +
> +	rtase_set_mar(tp);

Why this function at all? You can set the registers directly here.
Related question: Why store mc_filter[] in tp?
It's used nowhere else AFAICS.

> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
> +}
> +
>  static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
>  {
>  	const struct rtase_int_vector *ivec = &tp->int_vector[0];
> @@ -545,6 +681,113 @@ static void rtase_hw_reset(const struct net_device *dev)
>  	rtase_nic_reset(dev);
>  }
>
> +static void rtase_set_rx_queue(const struct rtase_private *tp)
> +{
> +	u16 reg_data;
> +
> +	reg_data = rtase_r16(tp, RTASE_FCR);
> +	switch (tp->func_rx_queue_num) {
> +	case 1:
> +		u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
> +		break;
> +	case 2:
> +		u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
> +		break;
> +	case 4:
> +		u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
> +		break;
> +	}
> +	rtase_w16(tp, RTASE_FCR, reg_data);
> +}
> +
> +static void rtase_set_tx_queue(const struct rtase_private *tp)
> +{
> +	u16 reg_data;
> +
> +	reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
> +	switch (tp->tx_queue_ctrl) {
> +	case 1:
> +		u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
> +		break;
> +	case 2:
> +		u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
> +		break;
> +	case 3:
> +	case 4:
> +		u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
> +		break;
> +	default:
> +		u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
> +		break;
> +	}
> +	rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
> +}
> +
> +static void rtase_hw_config(struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u32 reg_data32;
> +	u16 reg_data16;
> +
> +	rtase_hw_reset(dev);
> +
> +	/* Set Rx DMA burst */
> +	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
> +	reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
> +	u16p_replace_bits(&reg_data16, RX_DMA_BURST_256, RX_MX_DMA_MASK);
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
> +
> +	/* New Rx Descritpor */
> +	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
> +	reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
> +	u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
> +
> +	rtase_set_rx_queue(tp);
> +
> +	/* interrupt mitigation */
> +	rtase_interrupt_mitigation(tp);
> +
> +	/* set tx DMA burst size and interframe gap time */
> +	reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
> +	u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED, TX_DMA_MASK);
> +	u32p_replace_bits(&reg_data32, INTERFRAMEGAP, TX_INTER_FRAME_GAP_MASK);
> +	rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
> +
> +	/* new tx Descriptor */
> +	reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
> +	rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 | TX_NEW_DESC_FORMAT_EN);
> +
> +	/* tx Fetch Desc Number */
> +	rtase_w8(tp, RTASE_TDFNR, 0x10);
> +
> +	/* tag num select */
> +	reg_data16 = rtase_r16(tp, RTASE_MTPS);
> +	u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
> +	rtase_w16(tp, RTASE_MTPS, reg_data16);
> +
> +	rtase_set_tx_queue(tp);
> +
> +	/* TOK condition */
> +	rtase_w16(tp, RTASE_TOKSEL, 0x5555);
> +
> +	rtase_tally_counter_addr_fill(tp);
> +	rtase_desc_addr_fill(tp);
> +	rtase_hw_set_features(dev, dev->features);
> +
> +	/* enable flow control */
> +	reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
> +	reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> +	rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
> +	/* set Near FIFO Threshold - rx missed issue. */
> +	rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
> +
> +	rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
> +
> +	/* set Rx packet filter */
> +	rtase_hw_set_rx_packet_filter(dev);
> +}
> +
>  static void rtase_nic_enable(const struct net_device *dev)
>  {
>  	const struct rtase_private *tp = netdev_priv(dev);
> diff --git a/drivers/net/ethernet/realtek/rtase/tt.c b/drivers/net/ethernet/realtek/rtase/tt.c
> new file mode 100644
> index 000000000000..5ea4d51fcc47
> --- /dev/null
> +++ b/drivers/net/ethernet/realtek/rtase/tt.c
> @@ -0,0 +1,2542 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +/*
> + *  rtase is the Linux device driver released for Realtek Automotive Switch
> + *  controllers with PCI-Express interface.
> + *
> + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> + *
> + *  Below is a simplified block diagram of the chip and its relevant interfaces.
> + *
> + *               *************************
> + *               *                       *
> + *               *  CPU network device   *
> + *               *                       *
> + *               *   +-------------+     *
> + *               *   |  PCIE Host  |     *
> + *               ***********++************
> + *                          ||
> + *                         PCIE
> + *                          ||
> + *      ********************++**********************
> + *      *            | PCIE Endpoint |             *
> + *      *            +---------------+             *
> + *      *                | GMAC |                  *
> + *      *                +--++--+  Realtek         *
> + *      *                   ||     RTL90xx Series  *
> + *      *                   ||                     *
> + *      *     +-------------++----------------+    *
> + *      *     |           | MAC |             |    *
> + *      *     |           +-----+             |    *
> + *      *     |                               |    *
> + *      *     |     Ethernet Switch Core      |    *
> + *      *     |                               |    *
> + *      *     |   +-----+           +-----+   |    *
> + *      *     |   | MAC |...........| MAC |   |    *
> + *      *     +---+-----+-----------+-----+---+    *
> + *      *         | PHY |...........| PHY |        *
> + *      *         +--++-+           +--++-+        *
> + *      *************||****************||***********
> + *
> + *  The block of the Realtek RTL90xx series is our entire chip architecture,
> + *  the GMAC is connected to the switch core, and there is no PHY in between.
> + *  In addition, this driver is mainly used to control GMAC, but does not
> + *  control the switch core, so it is not the same as DSA.
> + */
> +
> +#include <linux/crc32.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/etherdevice.h>
> +#include <linux/if_vlan.h>
> +#include <linux/in.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <linux/mdio.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/netdevice.h>
> +#include <linux/pci.h>
> +#include <linux/prefetch.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/tcp.h>
> +#include <asm/irq.h>
> +#include <net/ip6_checksum.h>
> +#include <net/page_pool/helpers.h>
> +#include <net/pkt_cls.h>
> +
> +#include "rtase.h"
> +
> +#define RTK_OPTS1_DEBUG_VALUE 0x0BADBEEF
> +#define RTK_MAGIC_NUMBER      0x0BADBADBADBADBAD
> +
> +static const struct pci_device_id rtase_pci_tbl[] = {
> +	{PCI_VDEVICE(REALTEK, 0x906A)},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, rtase_pci_tbl);
> +
> +MODULE_AUTHOR("Realtek ARD Software Team");
> +MODULE_DESCRIPTION("Network Driver for the PCIe interface of Realtek Automotive Ethernet Switch");
> +MODULE_LICENSE("Dual BSD/GPL");
> +
> +struct rtase_counters {
> +	__le64 tx_packets;
> +	__le64 rx_packets;
> +	__le64 tx_errors;
> +	__le32 rx_errors;
> +	__le16 rx_missed;
> +	__le16 align_errors;
> +	__le32 tx_one_collision;
> +	__le32 tx_multi_collision;
> +	__le64 rx_unicast;
> +	__le64 rx_broadcast;
> +	__le32 rx_multicast;
> +	__le16 tx_aborted;
> +	__le16 tx_underun;
> +} __packed;
> +

You defined this struct in patch 1 already in another source code file.
Why redefine it?

> +static void rtase_w8(const struct rtase_private *tp, u16 reg, u8 val8)
> +{
> +	writeb(val8, tp->mmio_addr + reg);
> +}
> +
> +static void rtase_w16(const struct rtase_private *tp, u16 reg, u16 val16)
> +{
> +	writew(val16, tp->mmio_addr + reg);
> +}
> +
> +static void rtase_w32(const struct rtase_private *tp, u16 reg, u32 val32)
> +{
> +	writel(val32, tp->mmio_addr + reg);
> +}
> +
> +static u8 rtase_r8(const struct rtase_private *tp, u16 reg)
> +{
> +	return readb(tp->mmio_addr + reg);
> +}
> +
> +static u16 rtase_r16(const struct rtase_private *tp, u16 reg)
> +{
> +	return readw(tp->mmio_addr + reg);
> +}
> +
> +static u32 rtase_r32(const struct rtase_private *tp, u16 reg)
> +{
> +	return readl(tp->mmio_addr + reg);
> +}
> +
> +static void rtase_set_rxbufsize(struct rtase_private *tp)
> +{
> +	tp->rx_buf_sz = RX_BUF_SIZE;
> +}
> +
> +static int rtase_alloc_desc(struct rtase_private *tp)
> +{
> +	struct pci_dev *pdev = tp->pdev;
> +	u32 i;
> +
> +	/* rx and tx descriptors needs 256 bytes alignment.
> +	 * dma_alloc_coherent provides more.
> +	 */
> +	for (i = 0; i < tp->func_tx_queue_num; i++) {
> +		tp->tx_ring[i].desc = dma_alloc_coherent(&pdev->dev,
> +							 RTASE_TX_RING_DESC_SIZE,
> +							 &tp->tx_ring[i].phy_addr,
> +							 GFP_KERNEL);
> +		if (!tp->tx_ring[i].desc)
> +			return -ENOMEM;
> +	}
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++) {
> +		tp->rx_ring[i].desc =
> +			dma_alloc_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
> +					   &tp->rx_ring[i].phy_addr,
> +					   GFP_KERNEL);
> +		if (!tp->rx_ring[i].desc)
> +			return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void rtase_free_desc(struct rtase_private *tp)
> +{
> +	struct pci_dev *pdev = tp->pdev;
> +	u32 i;
> +
> +	for (i = 0; i < tp->func_tx_queue_num; i++) {
> +		if (!tp->tx_ring[i].desc)
> +			continue;
> +
> +		dma_free_coherent(&pdev->dev, RTASE_TX_RING_DESC_SIZE,
> +				  tp->tx_ring[i].desc,
> +				  tp->tx_ring[i].phy_addr);
> +		tp->tx_ring[i].desc = NULL;
> +	}
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++) {
> +		if (!tp->rx_ring[i].desc)
> +			continue;
> +
> +		dma_free_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE,
> +				  tp->rx_ring[i].desc,
> +				  tp->rx_ring[i].phy_addr);
> +		tp->rx_ring[i].desc = NULL;
> +	}
> +}
> +
> +static void rtase_unmap_tx_skb(struct pci_dev *pdev, u32 len,
> +			       struct tx_desc *desc)
> +{
> +	dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len,
> +			 DMA_TO_DEVICE);
> +	desc->opts1 = cpu_to_le32(RTK_OPTS1_DEBUG_VALUE);
> +	desc->opts2 = 0x00;
> +	desc->addr = cpu_to_le64(RTK_MAGIC_NUMBER);
> +}
> +
> +static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n)
> +{
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	struct net_device *dev = tp->dev;
> +	struct tx_desc *desc_base = ring->desc;
> +	u32 i;
> +
> +	for (i = 0; i < n; i++) {
> +		u32 entry = (start + i) % NUM_DESC;
> +		struct tx_desc *desc = desc_base + entry;
> +		u32 len = ring->mis.len[entry];
> +		struct sk_buff *skb;
> +
> +		if (len == 0)
> +			continue;
> +
> +		rtase_unmap_tx_skb(tp->pdev, len, desc);
> +		ring->mis.len[entry] = 0;
> +		skb = ring->skbuff[entry];
> +		if (!skb)
> +			continue;
> +
> +		dev->stats.tx_dropped++;
> +		dev_kfree_skb_any(skb);
> +		ring->skbuff[entry] = NULL;
> +	}
> +}
> +
> +static void rtase_tx_clear(struct rtase_private *tp)
> +{
> +	struct rtase_ring *ring;
> +	u16 i;
> +
> +	for (i = 0; i < tp->func_tx_queue_num; i++) {
> +		ring = &tp->tx_ring[i];
> +		rtase_tx_clear_range(ring, ring->dirty_idx, NUM_DESC);
> +		ring->cur_idx = 0;
> +		ring->dirty_idx = 0;
> +	}
> +}
> +
> +static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
> +{
> +	u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RING_END;
> +
> +	desc->desc_status.opts2 = 0;
> +	/* force memory writes to complete before releasing descriptor */
> +	dma_wmb();
> +	WRITE_ONCE(desc->desc_cmd.opts1,
> +		   cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
> +}
> +
> +static bool rtase_tx_avail(struct rtase_ring *ring)
> +{
> +	u32 avail_num = READ_ONCE(ring->dirty_idx) + NUM_DESC -
> +			READ_ONCE(ring->cur_idx);
> +
> +	return avail_num > MAX_SKB_FRAGS;
> +}
> +
> +static int tx_handler(struct rtase_ring *ring, int budget)
> +{
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	struct net_device *dev = tp->dev;
> +	int workdone = 0;
> +	u32 dirty_tx;
> +	u32 tx_left;
> +
> +	dirty_tx = ring->dirty_idx;
> +	tx_left = READ_ONCE(ring->cur_idx) - dirty_tx;
> +
> +	while (tx_left > 0) {
> +		u32 entry = dirty_tx % NUM_DESC;
> +		struct tx_desc *desc = ring->desc +
> +				       sizeof(struct tx_desc) * entry;
> +		u32 len = ring->mis.len[entry];
> +		u32 status;
> +
> +		status = le32_to_cpu(desc->opts1);
> +
> +		if (status & DESC_OWN)
> +			break;
> +
> +		rtase_unmap_tx_skb(tp->pdev, len, desc);
> +		ring->mis.len[entry] = 0;
> +		if (ring->skbuff[entry]) {
> +			dev_consume_skb_any(ring->skbuff[entry]);
> +			ring->skbuff[entry] = NULL;
> +		}
> +
> +		dev->stats.tx_bytes += len;
> +		dev->stats.tx_packets++;
> +		dirty_tx++;
> +		tx_left--;
> +		workdone++;
> +
> +		if (workdone == budget)
> +			break;
> +	}
> +
> +	if (ring->dirty_idx != dirty_tx) {
> +		WRITE_ONCE(ring->dirty_idx, dirty_tx);
> +
> +		if (__netif_subqueue_stopped(dev, ring->index) &&
> +		    rtase_tx_avail(ring))
> +			netif_start_subqueue(dev, ring->index);
> +

Really no memory barriers needed in this function?

> +		if (ring->cur_idx != dirty_tx)
> +			rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
> +	}
> +
> +	return workdone;
> +}
> +
> +static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
> +{
> +	struct rtase_ring *ring = &tp->tx_ring[idx];
> +	struct tx_desc *desc;
> +	u32 i;
> +
> +	memset(ring->desc, 0x0, RTASE_TX_RING_DESC_SIZE);
> +	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
> +	ring->cur_idx = 0;
> +	ring->dirty_idx = 0;
> +	ring->index = idx;
> +
> +	for (i = 0; i < NUM_DESC; i++) {
> +		ring->mis.len[i] = 0;
> +		if ((NUM_DESC - 1) == i) {
> +			desc = ring->desc + sizeof(struct tx_desc) * i;
> +			desc->opts1 = cpu_to_le32(RING_END);
> +		}
> +	}
> +
> +	ring->ring_handler = tx_handler;
> +	if (idx < 4) {
> +		ring->ivec = &tp->int_vector[idx];
> +		list_add_tail(&ring->ring_entry,
> +			      &tp->int_vector[idx].ring_list);
> +	} else {
> +		ring->ivec = &tp->int_vector[0];
> +		list_add_tail(&ring->ring_entry, &tp->int_vector[0].ring_list);
> +	}
> +}
> +
> +static void rtase_map_to_asic(union rx_desc *desc, dma_addr_t mapping,
> +			      u32 rx_buf_sz)
> +{
> +	desc->desc_cmd.addr = cpu_to_le64(mapping);
> +	/* make sure the physical address has been updated */
> +	wmb();
> +	rtase_mark_to_asic(desc, rx_buf_sz);
> +}
> +
> +static void rtase_make_unusable_by_asic(union rx_desc *desc)
> +{
> +	desc->desc_cmd.addr = cpu_to_le64(RTK_MAGIC_NUMBER);
> +	desc->desc_cmd.opts1 &= ~cpu_to_le32(DESC_OWN | RSVD_MASK);
> +}
> +
> +static int rtase_alloc_rx_skb(const struct rtase_ring *ring,
> +			      struct sk_buff **p_sk_buff, union rx_desc *desc,
> +			      dma_addr_t *rx_phy_addr, u8 in_intr)
> +{
> +	struct rtase_int_vector *ivec = ring->ivec;
> +	const struct rtase_private *tp = ivec->tp;
> +	struct sk_buff *skb = NULL;
> +	struct page *page;
> +	dma_addr_t mapping;
> +	void *buf_addr;
> +	int ret = 0;
> +
> +	page = page_pool_dev_alloc_pages(tp->page_pool);
> +	if (!page) {
> +		netdev_err(tp->dev, "failed to alloc page\n");
> +		goto err_out;
> +	}
> +
> +	buf_addr = page_address(page);
> +	mapping = page_pool_get_dma_addr(page);
> +
> +	skb = build_skb(buf_addr, PAGE_SIZE);
> +	if (!skb) {
> +		page_pool_put_full_page(tp->page_pool, page, true);
> +		netdev_err(tp->dev, "failed to build skb\n");
> +		goto err_out;
> +	}
> +
> +	*p_sk_buff = skb;
> +	*rx_phy_addr = mapping;
> +	rtase_map_to_asic(desc, mapping, tp->rx_buf_sz);
> +
> +	return ret;
> +
> +err_out:
> +	if (skb)
> +		dev_kfree_skb(skb);
> +
> +	ret = -ENOMEM;
> +	rtase_make_unusable_by_asic(desc);
> +
> +	return ret;
> +}
> +
> +static u32 rtase_rx_ring_fill(struct rtase_ring *ring, u32 ring_start,
> +			      u32 ring_end, u8 in_intr)
> +{
> +	union rx_desc *desc_base = ring->desc;
> +	u32 cur;
> +
> +	for (cur = ring_start; ring_end - cur > 0; cur++) {
> +		u32 i = cur % NUM_DESC;
> +		union rx_desc *desc = desc_base + i;
> +		int ret;
> +
> +		if (ring->skbuff[i])
> +			continue;
> +
> +		ret = rtase_alloc_rx_skb(ring, &ring->skbuff[i], desc,
> +					 &ring->mis.data_phy_addr[i],
> +					 in_intr);
> +		if (ret)
> +			break;
> +	}
> +
> +	return cur - ring_start;
> +}
> +
> +static void rtase_mark_as_last_descriptor(union rx_desc *desc)
> +{
> +	desc->desc_cmd.opts1 |= cpu_to_le32(RING_END);
> +}
> +
> +static void rtase_rx_ring_clear(struct rtase_ring *ring)
> +{
> +	union rx_desc *desc;
> +	u32 i;
> +
> +	for (i = 0; i < NUM_DESC; i++) {
> +		desc = ring->desc + sizeof(union rx_desc) * i;
> +
> +		if (!ring->skbuff[i])
> +			continue;
> +
> +		dev_kfree_skb(ring->skbuff[i]);
> +
> +		ring->skbuff[i] = NULL;
> +
> +		rtase_make_unusable_by_asic(desc);
> +	}
> +}
> +
> +static int rtase_fragmented_frame(u32 status)
> +{
> +	return (status & (RX_FIRST_FRAG | RX_LAST_FRAG)) !=
> +		(RX_FIRST_FRAG | RX_LAST_FRAG);
> +}
> +
> +static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff *skb,
> +			  const union rx_desc *desc)
> +{
> +	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
> +
> +	/* rx csum offload */
> +	if (((opts2 & RX_V4F) && !(opts2 & RX_IPF)) || (opts2 & RX_V6F)) {
> +		if (((opts2 & RX_TCPT) && !(opts2 & RX_TCPF)) ||
> +		    ((opts2 & RX_UDPT) && !(opts2 & RX_UDPF))) {
> +			skb->ip_summed = CHECKSUM_UNNECESSARY;
> +		} else {
> +			skb->ip_summed = CHECKSUM_NONE;
> +		}
> +	} else {
> +		skb->ip_summed = CHECKSUM_NONE;
> +	}
> +}
> +
> +static void rtase_rx_vlan_skb(union rx_desc *desc, struct sk_buff *skb)
> +{
> +	u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
> +
> +	if (!(opts2 & RX_VLAN_TAG))
> +		return;
> +
> +	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & VLAN_TAG_MASK));
> +}
> +
> +static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb)
> +{
> +	struct rtase_int_vector *ivec = ring->ivec;
> +
> +	napi_gro_receive(&ivec->napi, skb);
> +}
> +
> +static int rx_handler(struct rtase_ring *ring, int budget)
> +{
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	u32 pkt_size, cur_rx, delta, entry, status;
> +	struct net_device *dev = tp->dev;
> +	union rx_desc *desc_base = ring->desc;
> +	struct sk_buff *skb;
> +	union rx_desc *desc;
> +	int workdone = 0;
> +
> +	if (!ring->desc)
> +		return workdone;
> +
> +	cur_rx = ring->cur_idx;
> +	entry = cur_rx % NUM_DESC;
> +	desc = &desc_base[entry];
> +
> +	do {
> +		/* make sure discriptor has been updated */
> +		rmb();
> +		status = le32_to_cpu(desc->desc_status.opts1);
> +
> +		if (status & DESC_OWN)
> +			break;
> +
> +		if (unlikely(status & RX_RES)) {
> +			if (net_ratelimit())
> +				netdev_warn(dev, "Rx ERROR. status = %08x\n",
> +					    status);
> +
> +			dev->stats.rx_errors++;
> +
> +			if (status & (RX_RWT | RX_RUNT))
> +				dev->stats.rx_length_errors++;
> +
> +			if (status & RX_CRC)
> +				dev->stats.rx_crc_errors++;
> +
> +			if (dev->features & NETIF_F_RXALL)
> +				goto process_pkt;
> +
> +			rtase_mark_to_asic(desc, tp->rx_buf_sz);
> +			goto skip_process_pkt;
> +		}
> +
> +process_pkt:
> +		pkt_size = status & RX_PKT_SIZE_MASK;
> +		if (likely(!(dev->features & NETIF_F_RXFCS)))
> +			pkt_size -= ETH_FCS_LEN;
> +
> +		/* the driver does not support incoming fragmented
> +		 * frames. they are seen as a symptom of over-mtu
> +		 * sized frames
> +		 */
> +		if (unlikely(rtase_fragmented_frame(status))) {
> +			dev->stats.rx_dropped++;
> +			dev->stats.rx_length_errors++;
> +			rtase_mark_to_asic(desc, tp->rx_buf_sz);
> +			continue;
> +		}
> +
> +		skb = ring->skbuff[entry];
> +		dma_sync_single_for_cpu(&tp->pdev->dev,
> +					ring->mis.data_phy_addr[entry],
> +					tp->rx_buf_sz, DMA_FROM_DEVICE);
> +
> +		ring->skbuff[entry] = NULL;
> +
> +		if (dev->features & NETIF_F_RXCSUM)
> +			rtase_rx_csum(tp, skb, desc);
> +
> +		skb->dev = dev;
> +		skb_put(skb, pkt_size);
> +		skb_mark_for_recycle(skb);
> +		skb->protocol = eth_type_trans(skb, dev);
> +
> +		if (skb->pkt_type == PACKET_MULTICAST)
> +			dev->stats.multicast++;
> +
> +		rtase_rx_vlan_skb(desc, skb);
> +		rtase_rx_skb(ring, skb);
> +
> +		dev->stats.rx_bytes += pkt_size;
> +		dev->stats.rx_packets++;
> +
> +skip_process_pkt:
> +		workdone++;
> +		cur_rx++;
> +		entry = cur_rx % NUM_DESC;
> +		desc = ring->desc + sizeof(union rx_desc) * entry;
> +		prefetch(desc);
> +	} while (workdone != budget);
> +
> +	ring->cur_idx = cur_rx;
> +	delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx, 1);
> +
> +	if (!delta && workdone)
> +		netdev_info(dev, "no Rx buffer allocated\n");
> +
> +	ring->dirty_idx += delta;
> +
> +	if ((ring->dirty_idx + NUM_DESC) == ring->cur_idx)
> +		netdev_emerg(dev, "Rx buffers exhausted\n");
> +
> +	return workdone;
> +}
> +
> +static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
> +{
> +	struct rtase_ring *ring = &tp->rx_ring[idx];
> +	u16 i;
> +
> +	memset(ring->desc, 0x0, RTASE_RX_RING_DESC_SIZE);
> +	memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
> +	ring->cur_idx = 0;
> +	ring->dirty_idx = 0;
> +	ring->index = idx;
> +
> +	for (i = 0; i < NUM_DESC; i++)
> +		ring->mis.data_phy_addr[i] = 0;
> +
> +	ring->ring_handler = rx_handler;
> +	ring->ivec = &tp->int_vector[idx];
> +	list_add_tail(&ring->ring_entry, &tp->int_vector[idx].ring_list);
> +}
> +
> +static void rtase_rx_clear(struct rtase_private *tp)
> +{
> +	u32 i;
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++)
> +		rtase_rx_ring_clear(&tp->rx_ring[i]);
> +
> +	page_pool_destroy(tp->page_pool);
> +	tp->page_pool = NULL;
> +}
> +
> +static int rtase_init_ring(const struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	struct page_pool *page_pool;
> +	struct page_pool_params pp_params = {
> +		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
> +		.order = 0,
> +		.pool_size = NUM_DESC * tp->func_rx_queue_num,
> +		.nid = dev_to_node(&tp->pdev->dev),
> +		.dev = &tp->pdev->dev,
> +		.dma_dir = DMA_FROM_DEVICE,
> +		.max_len = PAGE_SIZE,
> +		.offset = 0,
> +	};
> +	u32 num;
> +	u16 i;
> +
> +	page_pool = page_pool_create(&pp_params);
> +	if (IS_ERR(page_pool)) {
> +		netdev_err(tp->dev, "failed to create page pool\n");
> +		return -ENOMEM;
> +	}
> +
> +	tp->page_pool = page_pool;
> +
> +	for (i = 0; i < tp->func_tx_queue_num; i++)
> +		rtase_tx_desc_init(tp, i);
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++) {
> +		rtase_rx_desc_init(tp, i);
> +		num = rtase_rx_ring_fill(&tp->rx_ring[i], 0, NUM_DESC, 0);
> +		if (num != NUM_DESC)
> +			goto err_out;
> +
> +		rtase_mark_as_last_descriptor(tp->rx_ring[i].desc +
> +					      sizeof(union rx_desc) *
> +					      (NUM_DESC - 1));
> +	}
> +
> +	return 0;
> +
> +err_out:
> +	rtase_rx_clear(tp);
> +	return -ENOMEM;
> +}
> +
> +static void rtase_interrupt_mitigation(const struct rtase_private *tp)
> +{
> +	u32 i;
> +
> +	/* tx interrupt mitigation */
> +	for (i = 0; i < tp->func_tx_queue_num; i++)
> +		rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
> +
> +	/* rx interrupt mitigation */
> +	for (i = 0; i < tp->func_rx_queue_num; i++)
> +		rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
> +}
> +
> +static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
> +{
> +	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> +	rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
> +}
> +
> +static void rtase_tally_counter_clear(const struct rtase_private *tp)
> +{
> +	u32 cmd = lower_32_bits(tp->tally_paddr);
> +
> +	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> +	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
> +}
> +
> +static void rtase_desc_addr_fill(const struct rtase_private *tp)
> +{
> +	const struct rtase_ring *ring;
> +	u16 i, cmd, val;
> +	int err;
> +
> +	for (i = 0; i < tp->func_tx_queue_num; i++) {
> +		ring = &tp->tx_ring[i];
> +
> +		rtase_w32(tp, RTASE_TX_DESC_ADDR0,
> +			  lower_32_bits(ring->phy_addr));
> +		rtase_w32(tp, RTASE_TX_DESC_ADDR4,
> +			  upper_32_bits(ring->phy_addr));
> +
> +		cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
> +		rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
> +
> +		err = read_poll_timeout(rtase_r16, val, !(val & TX_DESC_CMD_CS),
> +					10, 1000, false, tp, RTASE_TX_DESC_COMMAND);
> +
> +		if (err == -ETIMEDOUT)
> +			netdev_err(tp->dev, "error occurred in fill tx descriptor\n");
> +	}
> +
> +	for (i = 0; i < tp->func_rx_queue_num; i++) {
> +		ring = &tp->rx_ring[i];
> +
> +		if (i == 0) {
> +			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
> +				  lower_32_bits(ring->phy_addr));
> +			rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
> +				  upper_32_bits(ring->phy_addr));
> +		} else {
> +			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i - 1) * 8)),
> +				  lower_32_bits(ring->phy_addr));
> +			rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i - 1) * 8)),
> +				  upper_32_bits(ring->phy_addr));
> +		}
> +	}
> +}
> +
> +static void rtase_hw_set_features(const struct net_device *dev,
> +				  netdev_features_t features)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 rx_config, val;
> +
> +	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> +	if (features & NETIF_F_RXALL)
> +		rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
> +	else
> +		rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
> +
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> +
> +	val = rtase_r16(tp, RTASE_CPLUS_CMD);
> +	if (features & NETIF_F_RXCSUM)
> +		rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
> +	else
> +		rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
> +
> +	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
> +	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
> +		rx_config |= (INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
> +	else
> +		rx_config &= ~(INNER_VLAN_DETAG_EN | OUTER_VLAN_DETAG_EN);
> +
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
> +}
> +
> +static void rtase_set_mar(const struct rtase_private *tp)
> +{
> +	rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
> +	rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
> +}
> +
> +static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
> +	u16 rx_mode;
> +
> +	rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
> +	rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
> +	} else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
> +		   (dev->flags & IFF_ALLMULTI)) {
> +		/* too many to filter perfectly -- accept all multicasts */
> +		rx_mode |= ACCEPT_MULTICAST;
> +	} else {
> +		struct netdev_hw_addr *hw_addr;
> +
> +		mc_filter[0] = 0;
> +		mc_filter[1] = 0;
> +
> +		netdev_for_each_mc_addr(hw_addr, dev) {
> +			u32 bit_nr = eth_hw_addr_crc(hw_addr);
> +			u32 idx = u32_get_bits(bit_nr, BIT(31));
> +			u32 bit = u32_get_bits(bit_nr, MULTICAST_FILTER_MASK);
> +
> +			mc_filter[idx] |= BIT(bit);
> +			rx_mode |= ACCEPT_MULTICAST;
> +		}
> +	}
> +
> +	if (dev->features & NETIF_F_RXALL)
> +		rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
> +
> +	tp->mc_filter[0] = swab32(mc_filter[1]);
> +	tp->mc_filter[1] = swab32(mc_filter[0]);
> +
> +	rtase_set_mar(tp);
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
> +}
> +

This function is define in rt_main already. You seem to duplicate a lot
of code. Why?

> +static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
> +{
> +	const struct rtase_int_vector *ivec = &tp->int_vector[0];
> +	u32 val1;
> +	u16 val2;
> +	u8 i;
> +
> +	rtase_w32(tp, ivec->imr_addr, 0);
> +	val1 = rtase_r32(tp, ivec->isr_addr);
> +	rtase_w32(tp, ivec->isr_addr, val1);
> +
> +	for (i = 1; i < tp->int_nums; i++) {
> +		ivec = &tp->int_vector[i];
> +		rtase_w16(tp, ivec->imr_addr, 0);
> +		val2 = rtase_r16(tp, ivec->isr_addr);
> +		rtase_w16(tp, ivec->isr_addr, val2);
> +	}
> +}
> +
> +static void rtase_poll_timeout(const struct rtase_private *tp, u32 cond,
> +			       u32 sleep_us, u64 timeout_us, u16 reg)
> +{
> +	int err;
> +	u8 val;
> +
> +	err = read_poll_timeout(rtase_r8, val, val & cond, sleep_us, timeout_us,
> +				false, tp, reg);
> +
> +	if (err == -ETIMEDOUT)
> +		netdev_err(tp->dev, "poll reg 0x00%x timeout\n", reg);
> +}
> +
> +static void rtase_nic_reset(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 rx_config;
> +	u8 val;
> +
> +	rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config & ~ACCEPT_MASK);
> +
> +	val = rtase_r8(tp, RTASE_MISC);
> +	rtase_w8(tp, RTASE_MISC, val | RX_DV_GATE_EN);
> +
> +	val = rtase_r8(tp, RTASE_CHIP_CMD);
> +	rtase_w8(tp, RTASE_CHIP_CMD, val | STOP_REQ);
> +	mdelay(2);
> +
> +	rtase_poll_timeout(tp, STOP_REQ_DONE, 100, 150000, RTASE_CHIP_CMD);
> +
> +	rtase_poll_timeout(tp, TX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
> +
> +	rtase_poll_timeout(tp, RX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
> +
> +	val = rtase_r8(tp, RTASE_CHIP_CMD);
> +	rtase_w8(tp, RTASE_CHIP_CMD, val & ~(TE | RE));
> +	val = rtase_r8(tp, RTASE_CHIP_CMD);
> +	rtase_w8(tp, RTASE_CHIP_CMD, val & ~STOP_REQ);
> +
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> +}
> +
> +static void rtase_hw_reset(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	rtase_irq_dis_and_clear(tp);
> +
> +	rtase_nic_reset(dev);
> +}
> +
> +static void rtase_set_rx_queue(const struct rtase_private *tp)
> +{
> +	u16 reg_data;
> +
> +	reg_data = rtase_r16(tp, RTASE_FCR);
> +	switch (tp->func_rx_queue_num) {
> +	case 1:
> +		u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
> +		break;
> +	case 2:
> +		u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
> +		break;
> +	case 4:
> +		u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
> +		break;
> +	}
> +	rtase_w16(tp, RTASE_FCR, reg_data);
> +}
> +
> +static void rtase_set_tx_queue(const struct rtase_private *tp)
> +{
> +	u16 reg_data;
> +
> +	reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
> +	switch (tp->tx_queue_ctrl) {
> +	case 1:
> +		u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
> +		break;
> +	case 2:
> +		u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
> +		break;
> +	case 3:
> +	case 4:
> +		u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
> +		break;
> +	default:
> +		u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
> +		break;
> +	}
> +	rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
> +}
> +
> +static void rtase_hw_config(struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u32 reg_data32;
> +	u16 reg_data16;
> +
> +	rtase_hw_reset(dev);
> +
> +	/* Set Rx DMA burst */
> +	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
> +	reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
> +	u16p_replace_bits(&reg_data16, RX_DMA_BURST_256, RX_MX_DMA_MASK);
> +	rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
> +
> +	/* New Rx Descritpor */
> +	reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
> +	reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
> +	u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
> +
> +	rtase_set_rx_queue(tp);
> +
> +	/* interrupt mitigation */
> +	rtase_interrupt_mitigation(tp);
> +
> +	/* set tx DMA burst size and interframe gap time */
> +	reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
> +	u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED, TX_DMA_MASK);
> +	u32p_replace_bits(&reg_data32, INTERFRAMEGAP, TX_INTER_FRAME_GAP_MASK);
> +	rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
> +
> +	/* new tx Descriptor */
> +	reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
> +	rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 | TX_NEW_DESC_FORMAT_EN);
> +
> +	/* tx Fetch Desc Number */
> +	rtase_w8(tp, RTASE_TDFNR, 0x10);
> +
> +	/* tag num select */
> +	reg_data16 = rtase_r16(tp, RTASE_MTPS);
> +	u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
> +	rtase_w16(tp, RTASE_MTPS, reg_data16);
> +
> +	rtase_set_tx_queue(tp);
> +
> +	/* TOK condition */
> +	rtase_w16(tp, RTASE_TOKSEL, 0x5555);
> +
> +	rtase_tally_counter_addr_fill(tp);
> +	rtase_desc_addr_fill(tp);
> +	rtase_hw_set_features(dev, dev->features);
> +
> +	/* enable flow control */
> +	reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
> +	reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> +	rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
> +	/* set Near FIFO Threshold - rx missed issue. */
> +	rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
> +
> +	rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
> +
> +	/* set Rx packet filter */
> +	rtase_hw_set_rx_packet_filter(dev);
> +}
> +
> +static void rtase_nic_enable(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 rcr = rtase_r16(tp, RTASE_RX_CONFIG_1);
> +	u8 val;
> +
> +	/* PCIe PLA reload */
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr & ~PCIE_RELOAD_En);
> +	rtase_w16(tp, RTASE_RX_CONFIG_1, rcr | PCIE_RELOAD_En);
> +
> +	/* set PCIe TE & RE */
> +	val = rtase_r8(tp, RTASE_CHIP_CMD);
> +	rtase_w8(tp, RTASE_CHIP_CMD, val | TE | RE);
> +
> +	/* clear rxdv_gated_en */
> +	val = rtase_r8(tp, RTASE_MISC);
> +	rtase_w8(tp, RTASE_MISC, val & ~RX_DV_GATE_EN);
> +}
> +
> +static void rtase_enable_hw_interrupt(const struct rtase_private *tp)
> +{
> +	const struct rtase_int_vector *ivec = &tp->int_vector[0];
> +	u32 i;
> +
> +	rtase_w32(tp, ivec->imr_addr, ivec->imr);
> +
> +	for (i = 1; i < tp->int_nums; i++) {
> +		ivec = &tp->int_vector[i];
> +		rtase_w16(tp, ivec->imr_addr, ivec->imr);
> +	}
> +}
> +
> +static void rtase_hw_start(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	rtase_nic_enable(dev);
> +	rtase_enable_hw_interrupt(tp);
> +}
> +
> +/*  the interrupt handler does RXQ0 and TXQ0, TXQ4~7 interrutp status
> + */
> +static irqreturn_t rtase_interrupt(int irq, void *dev_instance)
> +{
> +	const struct rtase_private *tp;
> +	struct rtase_int_vector *ivec;
> +	u32 status;
> +
> +	ivec = dev_instance;
> +	tp = ivec->tp;
> +	status = rtase_r32(tp, ivec->isr_addr);
> +
> +	rtase_w32(tp, ivec->imr_addr, 0x0);
> +	rtase_w32(tp, ivec->isr_addr, status & ~FOVW);
> +
> +	if (napi_schedule_prep(&ivec->napi))
> +		__napi_schedule(&ivec->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*  the interrupt handler does RXQ1&TXQ1 or RXQ2&TXQ2 or RXQ3&TXQ3 interrupt
> + *  status according to interrupt vector
> + */
> +static irqreturn_t rtase_q_interrupt(int irq, void *dev_instance)
> +{
> +	const struct rtase_private *tp;
> +	struct rtase_int_vector *ivec;
> +	u16 status;
> +
> +	ivec = dev_instance;
> +	tp = ivec->tp;
> +	status = rtase_r16(tp, ivec->isr_addr);
> +
> +	rtase_w16(tp, ivec->imr_addr, 0x0);
> +	rtase_w16(tp, ivec->isr_addr, status);
> +
> +	if (napi_schedule_prep(&ivec->napi))
> +		__napi_schedule(&ivec->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rtase_poll(struct napi_struct *napi, int budget)
> +{
> +	const struct rtase_int_vector *ivec;
> +	const struct rtase_private *tp;
> +	struct rtase_ring *ring;
> +	int total_workdone = 0;
> +
> +	ivec = container_of(napi, struct rtase_int_vector, napi);
> +	tp = ivec->tp;
> +
> +	list_for_each_entry(ring, &ivec->ring_list, ring_entry) {
> +		total_workdone += ring->ring_handler(ring, budget);
> +	}
> +
> +	if (total_workdone >= budget)
> +		return budget;
> +
> +	if (napi_complete_done(napi, total_workdone)) {
> +		if (!ivec->index)
> +			rtase_w32(tp, ivec->imr_addr, ivec->imr);
> +		else
> +			rtase_w16(tp, ivec->imr_addr, ivec->imr);
> +	}
> +
> +	return total_workdone;
> +}
> +
> +static int rtase_open(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	struct rtase_int_vector *ivec = &tp->int_vector[0];
> +	const struct pci_dev *pdev = tp->pdev;
> +	int ret;
> +	u16 i;
> +
> +	rtase_set_rxbufsize(tp);
> +
> +	ret = rtase_alloc_desc(tp);
> +	if (ret)
> +		goto err_free_all_allocated_mem;
> +
> +	ret = rtase_init_ring(dev);
> +	if (ret)
> +		goto err_free_all_allocated_mem;
> +
> +	INIT_DELAYED_WORK(&tp->task, NULL);
> +

Why not just init once in probe()?

> +	rtase_hw_config(dev);
> +
> +	if (tp->sw_flag & SWF_MSIX_ENABLED) {
> +		ret = request_irq(ivec->irq, rtase_interrupt, 0,
> +				  dev->name, ivec);
> +
> +		/* request other interrupts to handle multiqueue */
> +		for (i = 1; i < tp->int_nums; i++) {
> +			if (ret)
> +				continue;
> +
> +			ivec = &tp->int_vector[i];
> +			if (ivec->status != 1)
> +				continue;
> +
> +			snprintf(ivec->name, sizeof(ivec->name), "%s_int%i", tp->dev->name, i);
> +			ret = request_irq(ivec->irq, rtase_q_interrupt, 0,
> +					  ivec->name, ivec);
> +		}
> +	} else if (tp->sw_flag & SWF_MSI_ENABLED) {
> +		ret = request_irq(pdev->irq, rtase_interrupt, 0, dev->name,
> +				  ivec);
> +	} else {
> +		ret = request_irq(pdev->irq, rtase_interrupt, IRQF_SHARED,
> +				  dev->name, ivec);
> +	}
> +
> +	if (ret != 0) {
> +		netdev_err(dev, "can't request MSIX interrupt. Error: %d\n", ret);
> +		goto err_free_all_allocated_mem;
> +	}
> +
> +	rtase_hw_start(dev);
> +
> +	netif_carrier_on(dev);
> +	netif_wake_queue(dev);
> +
> +	goto out;
> +
> +err_free_all_allocated_mem:
> +	rtase_free_desc(tp);
> +
> +out:
> +	return ret;
> +}
> +
> +static void rtase_down(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u32 i;
> +
> +	netif_stop_queue(dev);
> +
> +	/* give a racing hard_start_xmit a few cycles to complete */
> +	synchronize_rcu();
> +
> +	netif_carrier_off(dev);
> +
> +	rtase_hw_reset(dev);
> +
> +	for (i = 0; i < tp->int_nums; i++)
> +		synchronize_irq(tp->int_vector[i].irq);
> +
> +	rtase_tx_clear(tp);
> +
> +	rtase_rx_clear(tp);
> +}
> +
> +static int rtase_close(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	const struct pci_dev *pdev = tp->pdev;
> +	u32 i;
> +
> +	rtase_down(dev);
> +
> +	if (tp->sw_flag & SWF_MSIX_ENABLED) {
> +		for (i = 0; i < tp->int_nums; i++)
> +			free_irq(tp->int_vector[i].irq, &tp->int_vector[i]);
> +
> +	} else {
> +		free_irq(pdev->irq, &tp->int_vector[0]);
> +	}
> +
> +	rtase_free_desc(tp);
> +
> +	return 0;
> +}
> +
> +static u32 rtase_tx_vlan_tag(const struct rtase_private *tp,
> +			     const struct sk_buff *skb)
> +{
> +	return (skb_vlan_tag_present(skb)) ?
> +		(TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00;
> +}
> +
> +static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
> +{
> +	u8 ip_protocol;
> +	u32 csum_cmd;
> +
> +	switch (vlan_get_protocol(skb)) {
> +	case htons(ETH_P_IP):
> +		csum_cmd = TX_IPCS_C;
> +		ip_protocol = ip_hdr(skb)->protocol;
> +		break;
> +
> +	case htons(ETH_P_IPV6):
> +		csum_cmd = TX_IPV6F_C;
> +		ip_protocol = ipv6_hdr(skb)->nexthdr;
> +		break;
> +
> +	default:
> +		ip_protocol = IPPROTO_RAW;
> +		break;
> +	}
> +
> +	if (ip_protocol == IPPROTO_TCP)
> +		csum_cmd |= TX_TCPCS_C;
> +	else if (ip_protocol == IPPROTO_UDP)
> +		csum_cmd |= TX_UDPCS_C;
> +	else
> +		WARN_ON_ONCE(1);
> +
> +	csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
> +
> +	return csum_cmd;
> +}
> +
> +static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
> +			    u32 opts1, u32 opts2)
> +{
> +	const struct skb_shared_info *info = skb_shinfo(skb);
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	const u8 nr_frags = info->nr_frags;
> +	struct tx_desc *txd = NULL;
> +	u32 cur_frag, entry;
> +	u64 pkt_len_cnt = 0;
> +
> +	entry = ring->cur_idx;
> +	for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
> +		const skb_frag_t *frag = &info->frags[cur_frag];
> +		dma_addr_t mapping;
> +		u32 status, len;
> +		void *addr;
> +
> +		entry = (entry + 1) % NUM_DESC;
> +
> +		txd = ring->desc + sizeof(struct tx_desc) * entry;
> +		len = skb_frag_size(frag);
> +		addr = skb_frag_address(frag);
> +		mapping = dma_map_single(&tp->pdev->dev, addr, len,
> +					 DMA_TO_DEVICE);
> +
> +		if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
> +			if (unlikely(net_ratelimit()))
> +				netdev_err(tp->dev,
> +					   "Failed to map TX fragments DMA!\n");
> +
> +			goto err_out;
> +		}
> +
> +		if (((entry + 1) % NUM_DESC) == 0)
> +			status = (opts1 | len | RING_END);
> +		else
> +			status = opts1 | len;
> +
> +		if (cur_frag == (nr_frags - 1)) {
> +			ring->skbuff[entry] = skb;
> +			status |= TX_LAST_FRAG;
> +		}
> +
> +		ring->mis.len[entry] = len;
> +		txd->addr = cpu_to_le64(mapping);
> +		txd->opts2 = cpu_to_le32(opts2);
> +
> +		/* make sure the operating fields have been updated */
> +		wmb();
> +		txd->opts1 = cpu_to_le32(status);
> +		pkt_len_cnt += len;
> +	}
> +
> +	return cur_frag;
> +
> +err_out:
> +	rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
> +	return -EIO;
> +}
> +
> +static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
> +				    struct net_device *dev)
> +{
> +	struct skb_shared_info *shinfo = skb_shinfo(skb);
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u32 q_idx, entry, len, opts1, opts2;
> +	u32 mss = shinfo->gso_size;
> +	struct rtase_ring *ring;
> +	struct tx_desc *txd;
> +	dma_addr_t mapping;
> +	bool stop_queue;
> +	int frags;
> +
> +	/* multiqueues */
> +	q_idx = skb_get_queue_mapping(skb);
> +	ring = &tp->tx_ring[q_idx];
> +
> +	if (unlikely(!rtase_tx_avail(ring))) {
> +		if (net_ratelimit())
> +			netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
> +		goto err_stop;
> +	}
> +
> +	entry = ring->cur_idx % NUM_DESC;
> +	txd = ring->desc + sizeof(struct tx_desc) * entry;
> +
> +	opts1 = DESC_OWN;
> +	opts2 = rtase_tx_vlan_tag(tp, skb);
> +
> +	/* tcp segmentation offload (or tcp large send) */
> +	if (mss) {
> +		if (shinfo->gso_type & SKB_GSO_TCPV4) {
> +			opts1 |= GIANT_SEND_V4;
> +		} else if (shinfo->gso_type & SKB_GSO_TCPV6) {
> +			if (skb_cow_head(skb, 0))
> +				goto err_dma_0;
> +
> +			tcp_v6_gso_csum_prep(skb);
> +			opts1 |= GIANT_SEND_V6;
> +		} else {
> +			WARN_ON_ONCE(1);
> +		}
> +
> +		opts1 |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
> +		opts2 |= u32_encode_bits(mss, MSS_MASK);
> +	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +		opts2 |= rtase_tx_csum(skb, dev);
> +	}
> +
> +	frags = rtase_xmit_frags(ring, skb, opts1, opts2);
> +	if (unlikely(frags < 0))
> +		goto err_dma_0;
> +
> +	if (frags) {
> +		len = skb_headlen(skb);
> +		opts1 |= TX_FIRST_FRAG;
> +	} else {
> +		len = skb->len;
> +		ring->skbuff[entry] = skb;
> +		opts1 |= TX_FIRST_FRAG | TX_LAST_FRAG;
> +	}
> +
> +	if (((entry + 1) % NUM_DESC) == 0)
> +		opts1 |= (len | RING_END);
> +	else
> +		opts1 |= len;
> +
> +	mapping = dma_map_single(&tp->pdev->dev, skb->data, len,
> +				 DMA_TO_DEVICE);
> +
> +	if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
> +		if (unlikely(net_ratelimit()))
> +			netdev_err(dev, "Failed to map TX DMA!\n");
> +
> +		goto err_dma_1;
> +	}
> +
> +	ring->mis.len[entry] = len;
> +	txd->addr = cpu_to_le64(mapping);
> +	txd->opts2 = cpu_to_le32(opts2);
> +	txd->opts1 = cpu_to_le32(opts1 & ~DESC_OWN);
> +
> +	/* make sure the operating fields have been updated */
> +	wmb();
> +
> +	txd->opts1 = cpu_to_le32(opts1);
> +
> +	skb_tx_timestamp(skb);
> +
> +	/* tx needs to see descriptor changes before updated cur_idx */
> +	smp_wmb();
> +
> +	WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1);
> +
> +	stop_queue = !rtase_tx_avail(ring);
> +	if (unlikely(stop_queue))
> +		netif_stop_subqueue(dev, q_idx);
> +
> +	/* set polling bit */
> +	rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
> +
> +	if (unlikely(stop_queue)) {
> +		/* make sure cur_idx and dirty_idx have been updated */
> +		smp_rmb();
> +		if (rtase_tx_avail(ring))
> +			netif_start_subqueue(dev, q_idx);
> +	}
> +
> +	return NETDEV_TX_OK;
> +
> +err_dma_1:
> +	ring->skbuff[entry] = NULL;
> +	rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
> +
> +err_dma_0:
> +	dev->stats.tx_dropped++;
> +	dev_kfree_skb_any(skb);
> +	return NETDEV_TX_OK;
> +
> +err_stop:
> +	netif_stop_queue(dev);
> +	dev->stats.tx_dropped++;
> +	return NETDEV_TX_BUSY;
> +}
> +
> +static void rtase_set_rx_mode(struct net_device *dev)
> +{
> +	rtase_hw_set_rx_packet_filter(dev);
> +}
> +
> +static void rtase_enable_eem_write(const struct rtase_private *tp)
> +{
> +	u8 val;
> +
> +	val = rtase_r8(tp, RTASE_EEM);
> +	rtase_w8(tp, RTASE_EEM, val | EEM_UNLOCK);
> +}
> +
> +static void rtase_disable_eem_write(const struct rtase_private *tp)
> +{
> +	u8 val;
> +
> +	val = rtase_r8(tp, RTASE_EEM);
> +	rtase_w8(tp, RTASE_EEM, val & ~EEM_UNLOCK);
> +}
> +
> +static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
> +{
> +	u32 rar_low, rar_high;
> +
> +	rar_low = (u32)addr[0] | ((u32)addr[1] << 8) |
> +		  ((u32)addr[2] << 16) | ((u32)addr[3] << 24);
> +
> +	rar_high = (u32)addr[4] | ((u32)addr[5] << 8);
> +
> +	rtase_enable_eem_write(tp);
> +	rtase_w32(tp, RTASE_MAC0, rar_low);
> +	rtase_w32(tp, RTASE_MAC4, rar_high);
> +	rtase_disable_eem_write(tp);
> +	rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
> +}
> +
> +static int rtase_set_mac_address(struct net_device *dev, void *p)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	int ret;
> +
> +	ret = eth_mac_addr(dev, p);
> +	if (ret)
> +		return ret;
> +
> +	rtase_rar_set(tp, dev->dev_addr);
> +
> +	return 0;
> +}
> +
> +static int rtase_change_mtu(struct net_device *dev, int new_mtu)
> +{
> +	dev->mtu = new_mtu;
> +
> +	netdev_update_features(dev);
> +
> +	return 0;
> +}
> +
> +static void rtase_wait_for_quiescence(const struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	struct rtase_int_vector *ivec;
> +	u32 i;
> +
> +	for (i = 0; i < tp->int_nums; i++) {
> +		ivec = &tp->int_vector[i];
> +		synchronize_irq(ivec->irq);
> +		/* wait for any pending NAPI task to complete */
> +		napi_disable(&ivec->napi);
> +	}
> +
> +	rtase_irq_dis_and_clear(tp);
> +
> +	for (i = 0; i < tp->int_nums; i++) {
> +		ivec = &tp->int_vector[i];
> +		napi_enable(&ivec->napi);
> +	}
> +}
> +
> +static void rtase_sw_reset(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	int ret;
> +
> +	netif_stop_queue(dev);
> +	netif_carrier_off(dev);
> +	rtase_hw_reset(dev);
> +
> +	/* let's wait a bit while any (async) irq lands on */
> +	rtase_wait_for_quiescence(dev);
> +	rtase_tx_clear(tp);
> +	rtase_rx_clear(tp);
> +
> +	ret = rtase_init_ring(dev);
> +	if (ret) {
> +		netdev_err(dev, "unable to init ring\n");
> +		rtase_free_desc(tp);
> +		return;
> +	}
> +
> +	rtase_hw_config(dev);
> +	/* always link, so start to transmit & receive */
> +	rtase_hw_start(dev);
> +
> +	netif_carrier_on(dev);
> +	netif_wake_queue(dev);
> +}
> +
> +static void rtase_dump_tally_counter(const struct rtase_private *tp)
> +{
> +	dma_addr_t paddr = tp->tally_paddr;
> +	u32 cmd = lower_32_bits(paddr);
> +	u32 val;
> +	int err;
> +
> +	rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(paddr));
> +	rtase_w32(tp, RTASE_DTCCR0, cmd);
> +	rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_DUMP);
> +
> +	err = read_poll_timeout(rtase_r32, val, !(val & COUNTER_DUMP), 10, 250,
> +				false, tp, RTASE_DTCCR0);
> +
> +	if (err == -ETIMEDOUT)
> +		netdev_err(tp->dev, "error occurred in dump tally counter\n");
> +}
> +
> +static void rtase_dump_state(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters;
> +	int max_reg_size = RTASE_PCI_REGS_SIZE;
> +	const struct rtase_ring *ring;
> +	u32 dword_rd;
> +	int n = 0;
> +
> +	ring = &tp->tx_ring[0];
> +	netdev_err(dev, "Tx descriptor info:\n");
> +	netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
> +	netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
> +	netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
> +
> +	ring = &tp->rx_ring[0];
> +	netdev_err(dev, "Rx descriptor info:\n");
> +	netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
> +	netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
> +	netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
> +
> +	netdev_err(dev, "Device Registers:\n");
> +	netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp, RTASE_CHIP_CMD));
> +	netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
> +	netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
> +	netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
> +		   rtase_r16(tp, RTASE_BOOT_CTL));
> +	netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
> +		   rtase_r16(tp, RTASE_EPHY_ISR));
> +	netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
> +		   rtase_r16(tp, RTASE_EPHY_IMR));
> +	netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
> +		   rtase_r16(tp, RTASE_CLKSW_SET));
> +
> +	netdev_err(dev, "Dump PCI Registers:\n");
> +
> +	while (n < max_reg_size) {
> +		if ((n % RTASE_DWORD_MOD) == 0)
> +			netdev_err(tp->dev, "0x%03x:\n", n);
> +
> +		pci_read_config_dword(tp->pdev, n, &dword_rd);
> +		netdev_err(tp->dev, "%08x\n", dword_rd);
> +		n += 4;
> +	}
> +
> +	netdev_err(dev, "Dump tally counter:\n");
> +	counters = tp->tally_vaddr;
> +	rtase_dump_tally_counter(tp);
> +
> +	netdev_err(dev, "tx_packets %lld\n",
> +		   le64_to_cpu(counters->tx_packets));
> +	netdev_err(dev, "rx_packets %lld\n",
> +		   le64_to_cpu(counters->rx_packets));
> +	netdev_err(dev, "tx_errors %lld\n",
> +		   le64_to_cpu(counters->tx_errors));
> +	netdev_err(dev, "rx_missed %lld\n",
> +		   le64_to_cpu(counters->rx_missed));
> +	netdev_err(dev, "align_errors %lld\n",
> +		   le64_to_cpu(counters->align_errors));
> +	netdev_err(dev, "tx_one_collision %lld\n",
> +		   le64_to_cpu(counters->tx_one_collision));
> +	netdev_err(dev, "tx_multi_collision %lld\n",
> +		   le64_to_cpu(counters->tx_multi_collision));
> +	netdev_err(dev, "rx_unicast %lld\n",
> +		   le64_to_cpu(counters->rx_unicast));
> +	netdev_err(dev, "rx_broadcast %lld\n",
> +		   le64_to_cpu(counters->rx_broadcast));
> +	netdev_err(dev, "rx_multicast %lld\n",
> +		   le64_to_cpu(counters->rx_multicast));
> +	netdev_err(dev, "tx_aborted %lld\n",
> +		   le64_to_cpu(counters->tx_aborted));
> +	netdev_err(dev, "tx_underun %lld\n",
> +		   le64_to_cpu(counters->tx_underun));
> +}
> +
> +static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue)
> +{
> +	rtase_dump_state(dev);
> +	rtase_sw_reset(dev);
> +}
> +
> +static void rtase_get_stats64(struct net_device *dev,
> +			      struct rtnl_link_stats64 *stats)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters = tp->tally_vaddr;
> +
> +	if (!counters)
> +		return;
> +
> +	netdev_stats_to_stats64(stats, &dev->stats);
> +	dev_fetch_sw_netstats(stats, dev->tstats);
> +
> +	/* fetch additional counter values missing in stats collected by driver
> +	 * from tally counter
> +	 */
> +	rtase_dump_tally_counter(tp);
> +
> +	stats->tx_errors = le64_to_cpu(counters->tx_errors);
> +	stats->collisions = le32_to_cpu(counters->tx_multi_collision);
> +	stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted);
> +	stats->rx_missed_errors = le16_to_cpu(counters->rx_missed);
> +}
> +
> +static void rtase_enable_vlan_filter(const struct rtase_private *tp, bool enabled)
> +{
> +	u16 tmp;
> +
> +	if (enabled == 1) {
> +		tmp = rtase_r16(tp, RTASE_FCR);
> +		if (!(tmp & FCR_VLAN_FTR_EN))
> +			rtase_w16(tp, RTASE_FCR, tmp | FCR_VLAN_FTR_EN);
> +
> +		tmp = rtase_r16(tp, RTASE_PCPR);
> +		if (!(tmp & PCPR_VLAN_FTR_EN))
> +			rtase_w16(tp, RTASE_PCPR, tmp | PCPR_VLAN_FTR_EN);
> +	} else {
> +		tmp = rtase_r16(tp, RTASE_FCR);
> +		if (tmp & FCR_VLAN_FTR_EN)
> +			rtase_w16(tp, RTASE_FCR, tmp & ~FCR_VLAN_FTR_EN);
> +
> +		tmp = rtase_r16(tp, RTASE_PCPR);
> +		if (!(tmp & PCPR_VLAN_FTR_EN))
> +			rtase_w16(tp, RTASE_PCPR, tmp & ~PCPR_VLAN_FTR_EN);
> +	}
> +}
> +
> +static int rtase_vlan_rx_add_vid(struct net_device *dev, __be16 protocol,
> +				 u16 vid)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u16 tmp_mem, i;
> +
> +	if (be16_to_cpu(protocol) != ETH_P_8021Q)
> +		return -EINVAL;
> +
> +	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
> +		u16 addr, mask;
> +
> +		if (!(tp->vlan_filter_ctrl & BIT(i))) {
> +			tp->vlan_filter_ctrl |= BIT(i);
> +			tp->vlan_filter_vid[i] = vid;
> +			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4,
> +				  vid | VLAN_ENTRY_CAREBIT);
> +			/* each 16-bit register contains two VLAN entries */
> +			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
> +			mask = 0x1 << ((i & 0x1) * 8);
> +			tmp_mem = rtase_r16(tp, addr);
> +			tmp_mem |= mask;
> +			rtase_w16(tp, addr, tmp_mem);
> +			break;
> +		}
> +	}
> +
> +	if (i == RTASE_VLAN_FILTER_ENTRY_NUM)
> +		return -ENOENT;
> +
> +	rtase_enable_vlan_filter(tp, true);
> +
> +	return 0;
> +}
> +
> +static int rtase_vlan_rx_kill_vid(struct net_device *dev, __be16 protocol,
> +				  u16 vid)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u16 tmp_mem, i;
> +
> +	if (be16_to_cpu(protocol) != ETH_P_8021Q)
> +		return -EINVAL;
> +
> +	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
> +		u16 addr, mask;
> +
> +		if (tp->vlan_filter_vid[i] == vid) {
> +			tp->vlan_filter_ctrl &= ~BIT(i);
> +			tp->vlan_filter_vid[i] = 0;
> +			rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
> +
> +			/* each 16-bit register contains two VLAN entries */
> +			addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
> +			mask = ~(0x1 << ((i & 0x1) * 8));
> +			tmp_mem = rtase_r16(tp, addr);
> +			tmp_mem &= mask;
> +			rtase_w16(tp, addr, tmp_mem);
> +			break;
> +		}
> +	}
> +
> +	/* check vlan filter enabled */
> +	if (!tp->vlan_filter_ctrl)
> +		rtase_enable_vlan_filter(tp, false);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +/* Polling 'interrupt' - used by things like netconsole to send skbs
> + * without having to re-enable interrupts. It's not called while
> + * the interrupt routine is executing.
> + */
> +static void rtase_netpoll(struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	const struct pci_dev *pdev = tp->pdev;
> +
> +	disable_irq(pdev->irq);
> +	rtase_interrupt(pdev->irq, dev);
> +	enable_irq(pdev->irq);
> +}
> +#endif
> +
> +static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue)
> +{
> +	u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK;
> +	u32 val, i;
> +
> +	val = u32_encode_bits(idle / RTASE_1T_POWER, RTASE_IDLESLOPE_INT_MASK);
> +	idle %= RTASE_1T_POWER;
> +
> +	for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) {
> +		idle *= 2;
> +		if ((idle / RTASE_1T_POWER) == 1)
> +			val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i);
> +
> +		idle %= RTASE_1T_POWER;
> +	}
> +	rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val);
> +}
> +
> +static void rtase_setup_tc_cbs(struct rtase_private *tp,
> +			       const struct tc_cbs_qopt_offload *qopt)
> +{
> +	u32 queue = qopt->queue;
> +
> +	tp->tx_qos[queue].hicredit = qopt->hicredit;
> +	tp->tx_qos[queue].locredit = qopt->locredit;
> +	tp->tx_qos[queue].idleslope = qopt->idleslope;
> +	tp->tx_qos[queue].sendslope = qopt->sendslope;
> +
> +	/* set hardware cbs */
> +	rtase_set_hw_cbs(tp, queue);
> +}
> +
> +static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type,
> +			  void *type_data)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +
> +	switch (type) {
> +	case TC_SETUP_QDISC_CBS:
> +		rtase_setup_tc_cbs(tp, type_data);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static netdev_features_t rtase_fix_features(struct net_device *dev,
> +					    netdev_features_t features)
> +{
> +	netdev_features_t features_fix = features;
> +
> +	if (dev->mtu > MSS_MAX)
> +		features_fix &= ~NETIF_F_ALL_TSO;
> +
> +	if (dev->mtu > ETH_DATA_LEN)
> +		features_fix &= ~NETIF_F_ALL_TSO;
> +
> +	return features_fix;
> +}
> +
> +static int rtase_set_features(struct net_device *dev,
> +			      netdev_features_t features)
> +{
> +	netdev_features_t features_set = features;
> +
> +	features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM |
> +			NETIF_F_HW_VLAN_CTAG_RX;
> +
> +	if (features_set ^ dev->features)
> +		rtase_hw_set_features(dev, features_set);
> +
> +	return 0;
> +}
> +
> +static const struct net_device_ops rtase_netdev_ops = {
> +	.ndo_open = rtase_open,
> +	.ndo_stop = rtase_close,
> +	.ndo_start_xmit = rtase_start_xmit,
> +	.ndo_set_rx_mode = rtase_set_rx_mode,
> +	.ndo_set_mac_address = rtase_set_mac_address,
> +	.ndo_change_mtu = rtase_change_mtu,
> +	.ndo_tx_timeout = rtase_tx_timeout,
> +	.ndo_get_stats64 = rtase_get_stats64,
> +	.ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +	.ndo_poll_controller = rtase_netpoll,
> +#endif
> +	.ndo_setup_tc = rtase_setup_tc,
> +	.ndo_fix_features = rtase_fix_features,
> +	.ndo_set_features = rtase_set_features,
> +};
> +
> +static void rtase_get_mac_address(struct net_device *dev)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	u8 mac_addr[ETH_ALEN] __aligned(2) = {};
> +	u32 i;
> +
> +	for (i = 0; i < ETH_ALEN; i++)
> +		mac_addr[i] = rtase_r8(tp, RTASE_MAC0 + i);
> +
> +	if (!is_valid_ether_addr(mac_addr)) {
> +		eth_random_addr(mac_addr);
> +		dev->addr_assign_type = NET_ADDR_RANDOM;
> +		netdev_warn(dev, "Random ether addr %pM\n", mac_addr);
> +	}
> +
> +	eth_hw_addr_set(dev, mac_addr);
> +	rtase_rar_set(tp, mac_addr);
> +
> +	/* keep the original MAC address */
> +	ether_addr_copy(tp->org_mac_addr, dev->dev_addr);
> +	ether_addr_copy(dev->perm_addr, dev->dev_addr);
> +}
> +
> +static void rtase_get_drvinfo(struct net_device *dev,
> +			      struct ethtool_drvinfo *drvinfo)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
> +	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> +}
> +
> +static int rtase_get_settings(struct net_device *dev,
> +			      struct ethtool_link_ksettings *cmd)
> +{
> +	u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
> +
> +	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
> +						supported);
> +	cmd->base.speed = SPEED_5000;
> +	cmd->base.duplex = DUPLEX_FULL;
> +	cmd->base.port = PORT_MII;
> +	cmd->base.autoneg = AUTONEG_DISABLE;
> +
> +	return 0;
> +}
> +
> +static void rtase_get_pauseparam(struct net_device *dev,
> +				 struct ethtool_pauseparam *pause)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> +
> +	pause->autoneg = AUTONEG_DISABLE;
> +
> +	if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
> +	    (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
> +		pause->rx_pause = 1;
> +		pause->tx_pause = 1;
> +	} else if ((value & FORCE_TXFLOW_EN)) {
> +		pause->tx_pause = 1;
> +	} else if ((value & FORCE_RXFLOW_EN)) {
> +		pause->rx_pause = 1;
> +	}
> +}
> +
> +static int rtase_set_pauseparam(struct net_device *dev,
> +				struct ethtool_pauseparam *pause)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> +
> +	if (pause->autoneg)
> +		return -EOPNOTSUPP;
> +
> +	value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> +
> +	if (pause->tx_pause)
> +		value |= FORCE_TXFLOW_EN;
> +
> +	if (pause->rx_pause)
> +		value |= FORCE_RXFLOW_EN;
> +
> +	rtase_w16(tp, RTASE_CPLUS_CMD, value);
> +	return 0;
> +}
> +
> +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> +	"tx_packets",
> +	"rx_packets",
> +	"tx_errors",
> +	"rx_errors",
> +	"rx_missed",
> +	"align_errors",
> +	"tx_single_collisions",
> +	"tx_multi_collisions",
> +	"unicast",
> +	"broadcast",
> +	"multicast",
> +	"tx_aborted",
> +	"tx_underrun",
> +};
> +
> +static void rtase_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> +	switch (stringset) {
> +	case ETH_SS_STATS:
> +		memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
> +		break;
> +	}
> +}
> +
> +static int rtase_get_sset_count(struct net_device *dev, int sset)
> +{
> +	int ret = -EOPNOTSUPP;
> +
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		ret = ARRAY_SIZE(rtase_gstrings);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void rtase_get_ethtool_stats(struct net_device *dev,
> +				    struct ethtool_stats *stats, u64 *data)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters;
> +
> +	ASSERT_RTNL();
> +
> +	counters = tp->tally_vaddr;
> +	if (!counters)
> +		return;
> +
> +	rtase_dump_tally_counter(tp);
> +
> +	data[0] = le64_to_cpu(counters->tx_packets);
> +	data[1] = le64_to_cpu(counters->rx_packets);
> +	data[2] = le64_to_cpu(counters->tx_errors);
> +	data[3] = le32_to_cpu(counters->rx_errors);
> +	data[4] = le16_to_cpu(counters->rx_missed);
> +	data[5] = le16_to_cpu(counters->align_errors);
> +	data[6] = le32_to_cpu(counters->tx_one_collision);
> +	data[7] = le32_to_cpu(counters->tx_multi_collision);
> +	data[8] = le64_to_cpu(counters->rx_unicast);
> +	data[9] = le64_to_cpu(counters->rx_broadcast);
> +	data[10] = le32_to_cpu(counters->rx_multicast);
> +	data[11] = le16_to_cpu(counters->tx_aborted);
> +	data[12] = le16_to_cpu(counters->tx_underun);
> +}
> +
> +static const struct ethtool_ops rtase_ethtool_ops = {
> +	.get_drvinfo = rtase_get_drvinfo,
> +	.get_link = ethtool_op_get_link,
> +	.get_link_ksettings = rtase_get_settings,
> +	.get_pauseparam = rtase_get_pauseparam,
> +	.set_pauseparam = rtase_set_pauseparam,
> +	.get_strings = rtase_get_strings,
> +	.get_sset_count = rtase_get_sset_count,
> +	.get_ethtool_stats = rtase_get_ethtool_stats,
> +	.get_ts_info = ethtool_op_get_ts_info,
> +};
> +
> +static void rtase_init_netdev_ops(struct net_device *dev)
> +{
> +	dev->netdev_ops = &rtase_netdev_ops;
> +	dev->ethtool_ops = &rtase_ethtool_ops;
> +}
> +
> +static void rtase_reset_interrupt(struct pci_dev *pdev,
> +				  const struct rtase_private *tp)
> +{
> +	if (tp->sw_flag & SWF_MSIX_ENABLED)
> +		pci_disable_msix(pdev);
> +	else
> +		pci_disable_msi(pdev);
> +}
> +
> +static int rtase_alloc_msix(struct pci_dev *pdev, struct rtase_private *tp)
> +{
> +	int ret;
> +	u16 i;
> +
> +	memset(tp->msix_entry, 0x0, RTASE_NUM_MSIX * sizeof(struct msix_entry));
> +
> +	for (i = 0; i < RTASE_NUM_MSIX; i++)
> +		tp->msix_entry[i].entry = i;
> +
> +	ret = pci_enable_msix_range(pdev, tp->msix_entry, tp->int_nums,
> +				    tp->int_nums);
> +
> +	if (ret == tp->int_nums) {
> +		for (i = 0; i < tp->int_nums; i++) {
> +			tp->int_vector[i].irq = pci_irq_vector(pdev, i);
> +			tp->int_vector[i].status = 1;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int rtase_alloc_interrupt(struct pci_dev *pdev,
> +				 struct rtase_private *tp)
> +{
> +	int ret;
> +
> +	ret = rtase_alloc_msix(pdev, tp);
> +	if (ret != tp->int_nums) {
> +		ret = pci_enable_msi(pdev);
> +		if (ret)
> +			dev_err(&pdev->dev,
> +				"unable to alloc interrupt.(MSI)\n");
> +		else
> +			tp->sw_flag |= SWF_MSI_ENABLED;
> +	} else {
> +		tp->sw_flag |= SWF_MSIX_ENABLED;
> +	}
> +
> +	return ret;
> +}
> +
> +static void rtase_init_hardware(const struct rtase_private *tp)
> +{
> +	u16 i;
> +
> +	for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++)
> +		rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
> +}
> +
> +static void rtase_init_int_vector(struct rtase_private *tp)
> +{
> +	u16 i;
> +
> +	/* interrupt vector 0 */
> +	tp->int_vector[0].tp = tp;
> +	tp->int_vector[0].index = 0;
> +	tp->int_vector[0].imr_addr = RTASE_IMR0;
> +	tp->int_vector[0].isr_addr = RTASE_ISR0;
> +	tp->int_vector[0].imr = ROK | RDU | TOK | TOK4 | TOK5 | TOK6 | TOK7;
> +	tp->int_vector[0].poll = rtase_poll;
> +
> +	memset(tp->int_vector[0].name, 0x0, sizeof(tp->int_vector[0].name));
> +	INIT_LIST_HEAD(&tp->int_vector[0].ring_list);
> +
> +	netif_napi_add(tp->dev, &tp->int_vector[0].napi,
> +		       tp->int_vector[0].poll);
> +	napi_enable(&tp->int_vector[0].napi);
> +
> +	/* interrupt vector 1 ~ 3 */
> +	for (i = 1; i < tp->int_nums; i++) {
> +		tp->int_vector[i].tp = tp;
> +		tp->int_vector[i].index = i;
> +		tp->int_vector[i].imr_addr = RTASE_IMR1 + (i - 1) * 4;
> +		tp->int_vector[i].isr_addr = RTASE_ISR1 + (i - 1) * 4;
> +		tp->int_vector[i].imr = Q_ROK | Q_RDU | Q_TOK;
> +		tp->int_vector[i].poll = rtase_poll;
> +
> +		memset(tp->int_vector[i].name, 0x0, sizeof(tp->int_vector[0].name));
> +		INIT_LIST_HEAD(&tp->int_vector[i].ring_list);
> +
> +		netif_napi_add(tp->dev, &tp->int_vector[i].napi,
> +			       tp->int_vector[i].poll);
> +		napi_enable(&tp->int_vector[i].napi);
> +	}
> +}
> +
> +static u16 rtase_calc_time_mitigation(u32 time_us)
> +{
> +	u16 int_miti;
> +	u8 msb, time_count, time_unit;
> +
> +	time_us = min_t(int, time_us, MITI_MAX_TIME);
> +
> +	msb = fls(time_us);
> +	if (msb >= MITI_COUNT_BIT_NUM) {
> +		time_unit = msb - MITI_COUNT_BIT_NUM;
> +		time_count = time_us >> (msb - MITI_COUNT_BIT_NUM);
> +	} else {
> +		time_unit = 0;
> +		time_count = time_us;
> +	}
> +
> +	int_miti = u16_encode_bits(time_count, MITI_TIME_COUNT_MASK) |
> +		   u16_encode_bits(time_unit, MITI_TIME_UNIT_MASK);
> +
> +	return int_miti;
> +}
> +
> +static u16 rtase_calc_packet_num_mitigation(u16 pkt_num)
> +{
> +	u16 int_miti;
> +	u8 msb, pkt_num_count, pkt_num_unit;
> +
> +	pkt_num = min_t(int, pkt_num, MITI_MAX_PKT_NUM);
> +
> +	if (pkt_num > 60) {
> +		pkt_num_unit = MITI_MAX_PKT_NUM_IDX;
> +		pkt_num_count = pkt_num / MITI_MAX_PKT_NUM_UNIT;
> +	} else {
> +		msb = fls(pkt_num);
> +		if (msb >= MITI_COUNT_BIT_NUM) {
> +			pkt_num_unit = msb - MITI_COUNT_BIT_NUM;
> +			pkt_num_count = pkt_num >> (msb - MITI_COUNT_BIT_NUM);
> +		} else {
> +			pkt_num_unit = 0;
> +			pkt_num_count = pkt_num;
> +		}
> +	}
> +
> +	int_miti = u16_encode_bits(pkt_num_count, MITI_PKT_NUM_COUNT_MASK) |
> +		   u16_encode_bits(pkt_num_unit, MITI_PKT_NUM_UNIT_MASK);
> +
> +	return int_miti;
> +}
> +
> +static void rtase_init_software_variable(struct pci_dev *pdev,
> +					 struct rtase_private *tp)
> +{
> +	u16 int_miti;
> +
> +	tp->tx_queue_ctrl = RTASE_TXQ_CTRL;
> +	tp->func_tx_queue_num = RTASE_FUNC_TXQ_NUM;
> +	tp->func_rx_queue_num = RTASE_FUNC_RXQ_NUM;
> +	tp->int_nums = RTASE_INTERRUPT_NUM;
> +
> +	int_miti = rtase_calc_time_mitigation(MITI_DEFAULT_TIME) |
> +		   rtase_calc_packet_num_mitigation(MITI_DEFAULT_PKT_NUM);
> +	tp->tx_int_mit = int_miti;
> +	tp->rx_int_mit = int_miti;
> +
> +	tp->sw_flag = 0;
> +
> +	rtase_init_int_vector(tp);
> +
> +	/* MTU range: 60 - hw-specific max */
> +	tp->dev->min_mtu = ETH_ZLEN;
> +	tp->dev->max_mtu = MAX_JUMBO_SIZE;
> +}
> +
> +static bool rtase_check_mac_version_valid(struct rtase_private *tp)
> +{
> +	u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & HW_VER_MASK;
> +	bool known_ver = false;
> +
> +	switch (hw_ver) {
> +	case 0x00800000:
> +	case 0x04000000:
> +	case 0x04800000:
> +		known_ver = true;
> +		break;
> +	}
> +
> +	return known_ver;
> +}
> +
> +static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out,
> +			    void __iomem **ioaddr_out)
> +{
> +	struct net_device *dev;
> +	void __iomem *ioaddr;
> +	int ret = -ENOMEM;
> +
> +	/* dev zeroed in alloc_etherdev */
> +	dev = alloc_etherdev_mq(sizeof(struct rtase_private),
> +				RTASE_FUNC_TXQ_NUM);
> +	if (!dev)
> +		goto err_out;
> +
> +	SET_NETDEV_DEV(dev, &pdev->dev);
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret < 0)
> +		goto err_out_free_dev;
> +
> +	/* make sure PCI base addr 1 is MMIO */
> +	if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> +		ret = -ENODEV;
> +		goto err_out_disable;
> +	}
> +
> +	/* check for weird/broken PCI region reporting */
> +	if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
> +		ret = -ENODEV;
> +		goto err_out_disable;
> +	}
> +
> +	ret = pci_request_regions(pdev, KBUILD_MODNAME);
> +	if (ret < 0)
> +		goto err_out_disable;
> +
> +	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
> +		dev->features |= NETIF_F_HIGHDMA;
> +	else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
> +		goto err_out_free_res;
> +	else
> +		dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
> +
> +	pci_set_master(pdev);
> +
> +	/* ioremap MMIO region */
> +	ioaddr = ioremap(pci_resource_start(pdev, 2),
> +			 pci_resource_len(pdev, 2));
> +	if (!ioaddr) {
> +		ret = -EIO;
> +		goto err_out_free_res;
> +	}
> +
> +	*ioaddr_out = ioaddr;
> +	*dev_out = dev;
> +	goto out;
> +
> +err_out_free_res:
> +	pci_release_regions(pdev);
> +
> +err_out_disable:
> +	pci_disable_device(pdev);
> +
> +err_out_free_dev:
> +	free_netdev(dev);
> +
> +err_out:
> +	*ioaddr_out = NULL;
> +	*dev_out = NULL;
> +
> +out:
> +	return ret;
> +}
> +
> +static void rtase_release_board(struct pci_dev *pdev, struct net_device *dev,
> +				void __iomem *ioaddr)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	rtase_rar_set(tp, tp->org_mac_addr);
> +	iounmap(ioaddr);
> +
> +	if ((tp->sw_flag & SWF_MSIX_ENABLED))
> +		pci_disable_msix(pdev);
> +	else
> +		pci_disable_msi(pdev);
> +
> +	pci_release_regions(pdev);
> +	pci_disable_device(pdev);
> +	free_netdev(dev);
> +}
> +
> +static int rtase_init_one(struct pci_dev *pdev,
> +			  const struct pci_device_id *ent)
> +{
> +	struct net_device *dev = NULL;
> +	void __iomem *ioaddr = NULL;
> +	struct rtase_private *tp;
> +	int ret;
> +
> +	if (!pdev->is_physfn && pdev->is_virtfn) {
> +		dev_err(&pdev->dev, "This module does not support a virtual function.");
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(&pdev->dev, "Automotive Switch Ethernet driver loaded\n");
> +
> +	ret = rtase_init_board(pdev, &dev, &ioaddr);
> +	if (ret != 0)
> +		return ret;
> +
> +	tp = netdev_priv(dev);
> +	tp->mmio_addr = ioaddr;
> +	tp->dev = dev;
> +	tp->pdev = pdev;
> +
> +	/* identify chip attached to board */
> +	if (!rtase_check_mac_version_valid(tp)) {
> +		return dev_err_probe(&pdev->dev, -ENODEV,
> +				     "unknown chip version, contact rtase maintainers (see MAINTAINERS file)\n");
> +	}
> +
> +	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
> +	if (!dev->tstats)
> +		goto err_out_1;
> +
> +	rtase_init_software_variable(pdev, tp);
> +	rtase_init_hardware(tp);
> +
> +	ret = rtase_alloc_interrupt(pdev, tp);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "unable to alloc MSIX/MSI\n");
> +		goto err_out_1;
> +	}
> +
> +	rtase_init_netdev_ops(dev);
> +
> +	dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
> +
> +	dev->features |= NETIF_F_IP_CSUM;
> +	dev->features |= NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
> +	dev->features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
> +	dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
> +			   NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX |
> +			   NETIF_F_HW_VLAN_CTAG_RX;
> +	dev->hw_features |= NETIF_F_RXALL;
> +	dev->hw_features |= NETIF_F_RXFCS;
> +	dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
> +	dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
> +			     NETIF_F_HIGHDMA;
> +	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
> +	netif_set_tso_max_size(dev, LSO_64K);
> +	netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
> +
> +	rtase_get_mac_address(dev);
> +
> +	tp->tally_vaddr = dma_alloc_coherent(&pdev->dev,
> +					     sizeof(*tp->tally_vaddr),
> +					     &tp->tally_paddr,
> +					     GFP_KERNEL);
> +	if (!tp->tally_vaddr) {
> +		ret = -ENOMEM;
> +		goto err_out;
> +	}
> +
> +	rtase_tally_counter_clear(tp);
> +
> +	pci_set_drvdata(pdev, dev);
> +
> +	ret = register_netdev(dev);
> +	if (ret != 0)
> +		goto err_out;
> +
> +	netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq);
> +
> +	netif_carrier_off(dev);
> +
> +	goto out;
> +
> +err_out:
> +	if (tp->tally_vaddr) {
> +		dma_free_coherent(&pdev->dev,
> +				  sizeof(*tp->tally_vaddr),
> +				  tp->tally_vaddr,
> +				  tp->tally_paddr);
> +
> +		tp->tally_vaddr = NULL;
> +	}
> +
> +err_out_1:
> +	rtase_release_board(pdev, dev, ioaddr);
> +
> +out:
> +	return ret;
> +}
> +
> +static void rtase_remove_one(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct rtase_private *tp = netdev_priv(dev);
> +	struct rtase_int_vector *ivec;
> +	u32 i;
> +
> +	for (i = 0; i < tp->int_nums; i++) {
> +		ivec = &tp->int_vector[i];
> +		netif_napi_del(&ivec->napi);
> +	}
> +
> +	unregister_netdev(dev);
> +	rtase_reset_interrupt(pdev, tp);
> +	if (tp->tally_vaddr) {
> +		dma_free_coherent(&pdev->dev,
> +				  sizeof(*tp->tally_vaddr),
> +				  tp->tally_vaddr,
> +				  tp->tally_paddr);
> +		tp->tally_vaddr = NULL;
> +	}
> +
> +	rtase_release_board(pdev, dev, tp->mmio_addr);
> +	pci_set_drvdata(pdev, NULL);
> +}
> +
> +static void rtase_shutdown(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	if (netif_running(dev))
> +		rtase_close(dev);
> +
> +	rtase_reset_interrupt(pdev, tp);
> +}
> +
> +#ifdef CONFIG_PM
> +static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +
> +	if (!netif_running(dev))
> +		goto out;
> +
> +	netif_stop_queue(dev);
> +	netif_carrier_off(dev);
> +	netif_device_detach(dev);
> +	rtase_hw_reset(dev);
> +
> +out:
> +	pci_save_state(pdev);
> +
> +	return 0;
> +}
> +
> +static int rtase_resume(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct rtase_private *tp = netdev_priv(dev);
> +	int ret;
> +
> +	pci_set_power_state(pdev, PCI_D0);
> +	pci_restore_state(pdev);
> +	pci_enable_wake(pdev, PCI_D0, 0);
> +
> +	/* restore last modified mac address */
> +	rtase_rar_set(tp, dev->dev_addr);
> +
> +	if (!netif_running(dev))
> +		goto out;
> +
> +	rtase_wait_for_quiescence(dev);
> +
> +	rtase_tx_clear(tp);
> +	rtase_rx_clear(tp);
> +
> +	ret = rtase_init_ring(dev);
> +	if (ret) {
> +		netdev_err(dev, "unable to init ring\n");
> +		rtase_free_desc(tp);
> +		return -ENOMEM;
> +	}
> +
> +	rtase_hw_config(dev);
> +	/* always link, so start to transmit & receive */
> +	rtase_hw_start(dev);
> +
> +	netif_carrier_on(dev);
> +	netif_wake_queue(dev);
> +	netif_device_attach(dev);
> +
> +out:
> +	return 0;
> +}
> +#endif /* CONFIG_PM */
> +
> +static struct pci_driver rtase_pci_driver = {
> +	.name = KBUILD_MODNAME,
> +	.id_table = rtase_pci_tbl,
> +	.probe = rtase_init_one,
> +	.remove = rtase_remove_one,
> +	.shutdown = rtase_shutdown,
> +#ifdef CONFIG_PM
> +	.suspend = rtase_suspend,
> +	.resume = rtase_resume,
> +#endif
> +};
> +
> +module_pci_driver(rtase_pci_driver);
> diff --git a/drivers/net/ethernet/realtek/rtase/tt.h b/drivers/net/ethernet/realtek/rtase/tt.h
> new file mode 100644
> index 000000000000..9239c518c504
> --- /dev/null
> +++ b/drivers/net/ethernet/realtek/rtase/tt.h
> @@ -0,0 +1,353 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/*
> + *  rtase is the Linux device driver released for Realtek Automotive Switch
> + *  controllers with PCI-Express interface.
> + *
> + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> + */
> +
> +#ifndef _RTASE_H_
> +#define _RTASE_H_
> +
> +/* the low 32 bit address of receive buffer must be 8-byte alignment. */
> +#define RTK_RX_ALIGN 8
> +
> +#define HW_VER_MASK 0x7C800000
> +
> +#define RX_DMA_BURST_256       4
> +#define TX_DMA_BURST_UNLIMITED 7
> +#define RX_BUF_SIZE            (PAGE_SIZE - \
> +				SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
> +#define MAX_JUMBO_SIZE         (RX_BUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN)
> +
> +/* 3 means InterFrameGap = the shortest one */
> +#define INTERFRAMEGAP 0x03
> +
> +#define RTASE_REGS_SIZE     256
> +#define RTASE_PCI_REGS_SIZE 0x100
> +
> +#define MULTICAST_FILTER_MASK  GENMASK(30, 26)
> +#define MULTICAST_FILTER_LIMIT 32
> +
> +#define RTASE_VLAN_FILTER_ENTRY_NUM 32
> +#define RTASE_NUM_TX_QUEUE 8
> +#define RTASE_NUM_RX_QUEUE 4
> +
> +#define RTASE_TXQ_CTRL      1
> +#define RTASE_FUNC_TXQ_NUM  1
> +#define RTASE_FUNC_RXQ_NUM  1
> +#define RTASE_INTERRUPT_NUM 1
> +
> +#define MITI_TIME_COUNT_MASK     GENMASK(3, 0)
> +#define MITI_TIME_UNIT_MASK      GENMASK(7, 4)
> +#define MITI_DEFAULT_TIME        128
> +#define MITI_MAX_TIME            491520
> +#define MITI_PKT_NUM_COUNT_MASK  GENMASK(11, 8)
> +#define MITI_PKT_NUM_UNIT_MASK   GENMASK(13, 12)
> +#define MITI_DEFAULT_PKT_NUM     64
> +#define MITI_MAX_PKT_NUM_IDX     3
> +#define MITI_MAX_PKT_NUM_UNIT    16
> +#define MITI_MAX_PKT_NUM         240
> +#define MITI_COUNT_BIT_NUM       4
> +
> +#define RTASE_NUM_MSIX 4
> +
> +#define RTASE_DWORD_MOD 16
> +
> +/*****************************************************************************/
> +enum rtase_registers {
> +	RTASE_MAC0   = 0x0000,
> +	RTASE_MAC4   = 0x0004,
> +	RTASE_MAR0   = 0x0008,
> +	RTASE_MAR1   = 0x000C,
> +	RTASE_DTCCR0 = 0x0010,
> +	RTASE_DTCCR4 = 0x0014,
> +#define COUNTER_RESET BIT(0)
> +#define COUNTER_DUMP  BIT(3)
> +
> +	RTASE_FCR    = 0x0018,
> +#define FCR_RXQ_MASK    GENMASK(5, 4)
> +#define FCR_VLAN_FTR_EN BIT(1)
> +
> +	RTASE_LBK_CTRL = 0x001A,
> +#define LBK_ATLD BIT(1)
> +#define LBK_CLR  BIT(0)
> +
> +	RTASE_TX_DESC_ADDR0   = 0x0020,
> +	RTASE_TX_DESC_ADDR4   = 0x0024,
> +	RTASE_TX_DESC_COMMAND = 0x0028,
> +#define TX_DESC_CMD_CS BIT(15)
> +#define TX_DESC_CMD_WE BIT(14)
> +
> +	RTASE_BOOT_CTL  = 0x6004,
> +	RTASE_CLKSW_SET = 0x6018,
> +
> +	RTASE_CHIP_CMD = 0x0037,
> +#define STOP_REQ      BIT(7)
> +#define STOP_REQ_DONE BIT(6)
> +#define RE            BIT(3)
> +#define TE            BIT(2)
> +
> +	RTASE_IMR0 = 0x0038,
> +	RTASE_ISR0 = 0x003C,
> +#define TOK7 BIT(30)
> +#define TOK6 BIT(28)
> +#define TOK5 BIT(26)
> +#define TOK4 BIT(24)
> +#define FOVW BIT(6)
> +#define RDU  BIT(4)
> +#define TOK  BIT(2)
> +#define ROK  BIT(0)
> +
> +	RTASE_IMR1 = 0x0800,
> +	RTASE_ISR1 = 0x0802,
> +#define Q_TOK BIT(4)
> +#define Q_RDU BIT(1)
> +#define Q_ROK BIT(0)
> +
> +	RTASE_EPHY_ISR = 0x6014,
> +	RTASE_EPHY_IMR = 0x6016,
> +
> +	RTASE_TX_CONFIG_0 = 0x0040,
> +#define TX_INTER_FRAME_GAP_MASK GENMASK(25, 24)
> +	/* DMA burst value (0-7) is shift this many bits */
> +#define TX_DMA_MASK             GENMASK(10, 8)
> +
> +	RTASE_RX_CONFIG_0 = 0x0044,
> +#define RX_SINGLE_FETCH  BIT(14)
> +#define RX_SINGLE_TAG    BIT(13)
> +#define RX_MX_DMA_MASK   GENMASK(10, 8)
> +#define ACPT_FLOW        BIT(7)
> +#define ACCEPT_ERR       BIT(5)
> +#define ACCEPT_RUNT      BIT(4)
> +#define ACCEPT_BROADCAST BIT(3)
> +#define ACCEPT_MULTICAST BIT(2)
> +#define ACCEPT_MYPHYS    BIT(1)
> +#define ACCEPT_ALLPHYS   BIT(0)
> +#define ACCEPT_MASK      (ACPT_FLOW | ACCEPT_ERR | ACCEPT_RUNT | \
> +			  ACCEPT_BROADCAST | ACCEPT_MULTICAST | \
> +			  ACCEPT_MYPHYS | ACCEPT_ALLPHYS)
> +
> +	RTASE_RX_CONFIG_1 = 0x0046,
> +#define RX_MAX_FETCH_DESC_MASK  GENMASK(15, 11)
> +#define RX_NEW_DESC_FORMAT_EN   BIT(8)
> +#define OUTER_VLAN_DETAG_EN     BIT(7)
> +#define INNER_VLAN_DETAG_EN     BIT(6)
> +#define PCIE_NEW_FLOW           BIT(2)
> +#define PCIE_RELOAD_En          BIT(0)
> +
> +	RTASE_EEM = 0x0050,
> +#define EEM_UNLOCK 0xC0
> +
> +	RTASE_TDFNR  = 0x0057,
> +	RTASE_TPPOLL = 0x0090,
> +	RTASE_PDR    = 0x00B0,
> +	RTASE_FIFOR  = 0x00D3,
> +#define TX_FIFO_EMPTY BIT(5)
> +#define RX_FIFO_EMPTY BIT(4)
> +
> +	RTASE_PCPR = 0x00D8,
> +#define PCPR_VLAN_FTR_EN BIT(6)
> +
> +	RTASE_RMS       = 0x00DA,
> +	RTASE_CPLUS_CMD = 0x00E0,
> +#define FORCE_RXFLOW_EN BIT(11)
> +#define FORCE_TXFLOW_EN BIT(10)
> +#define RX_CHKSUM       BIT(5)
> +
> +	RTASE_Q0_RX_DESC_ADDR0 = 0x00E4,
> +	RTASE_Q0_RX_DESC_ADDR4 = 0x00E8,
> +	RTASE_Q1_RX_DESC_ADDR0 = 0x4000,
> +	RTASE_Q1_RX_DESC_ADDR4 = 0x4004,
> +	RTASE_MTPS             = 0x00EC,
> +#define TAG_NUM_SEL_MASK  GENMASK(10, 8)
> +
> +	RTASE_MISC = 0x00F2,
> +#define RX_DV_GATE_EN BIT(3)
> +
> +	RTASE_TFUN_CTRL = 0x0400,
> +#define TX_NEW_DESC_FORMAT_EN BIT(0)
> +
> +	RTASE_TX_CONFIG_1 = 0x203E,
> +#define TC_MODE_MASK  GENMASK(11, 10)
> +
> +	RTASE_TOKSEL      = 0x2046,
> +	RTASE_TXQCRDT_0   = 0x2500,
> +	RTASE_RFIFONFULL  = 0x4406,
> +	RTASE_INT_MITI_TX = 0x0A00,
> +	RTASE_INT_MITI_RX = 0x0A80,
> +
> +	RTASE_VLAN_ENTRY_MEM_0 = 0x7234,
> +	RTASE_VLAN_ENTRY_0     = 0xAC80,
> +};
> +
> +enum desc_status_bit {
> +	DESC_OWN = BIT(31), /* Descriptor is owned by NIC */
> +	RING_END = BIT(30), /* End of descriptor ring */
> +};
> +
> +enum sw_flag_content {
> +	SWF_MSI_ENABLED  = BIT(1),
> +	SWF_MSIX_ENABLED = BIT(2),
> +};
> +
> +#define RSVD_MASK 0x3FFFC000
> +
> +struct tx_desc {
> +	__le32 opts1;
> +	__le32 opts2;
> +	__le64 addr;
> +	__le32 opts3;
> +	__le32 reserved1;
> +	__le32 reserved2;
> +	__le32 reserved3;
> +} __packed;
> +
> +/*------ offset 0 of tx descriptor ------*/
> +#define TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */
> +#define TX_LAST_FRAG  BIT(28) /* Tx Final segment of a packet */
> +#define GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */
> +#define GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */
> +#define TX_VLAN_TAG   BIT(17) /* Add VLAN tag */
> +
> +/*------ offset 4 of tx descriptor ------*/
> +#define TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */
> +#define TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */
> +#define TX_IPCS_C  BIT(29) /* Calculate IP checksum */
> +#define TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */
> +
> +union rx_desc {
> +	struct {
> +		__le64 header_buf_addr;
> +		__le32 reserved1;
> +		__le32 opts_header_len;
> +		__le64 addr;
> +		__le32 reserved2;
> +		__le32 opts1;
> +	} __packed desc_cmd;
> +
> +	struct {
> +		__le32 reserved1;
> +		__le32 reserved2;
> +		__le32 rss;
> +		__le32 opts4;
> +		__le32 reserved3;
> +		__le32 opts3;
> +		__le32 opts2;
> +		__le32 opts1;
> +	} __packed desc_status;
> +} __packed;
> +
> +/*------ offset 28 of rx descriptor ------*/
> +#define RX_FIRST_FRAG    BIT(25) /* Rx First segment of a packet */
> +#define RX_LAST_FRAG     BIT(24) /* Rx Final segment of a packet */
> +#define RX_RES           BIT(20)
> +#define RX_RUNT          BIT(19)
> +#define RX_RWT           BIT(18)
> +#define RX_CRC           BIT(16)
> +#define RX_V6F           BIT(31)
> +#define RX_V4F           BIT(30)
> +#define RX_UDPT          BIT(29)
> +#define RX_TCPT          BIT(28)
> +#define RX_IPF           BIT(26) /* IP checksum failed */
> +#define RX_UDPF          BIT(25) /* UDP/IP checksum failed */
> +#define RX_TCPF          BIT(24) /* TCP/IP checksum failed */
> +#define RX_LBK_FIFO_FULL BIT(17) /* Loopback FIFO Full */
> +#define RX_VLAN_TAG      BIT(16) /* VLAN tag available */
> +
> +#define NUM_DESC                1024
> +#define RTASE_TX_RING_DESC_SIZE (NUM_DESC * sizeof(struct tx_desc))
> +#define RTASE_RX_RING_DESC_SIZE (NUM_DESC * sizeof(union rx_desc))
> +#define VLAN_ENTRY_CAREBIT      0xF0000000
> +#define VLAN_TAG_MASK           GENMASK(15, 0)
> +#define RX_PKT_SIZE_MASK        GENMASK(13, 0)
> +
> +/* txqos hardware definitions */
> +#define RTASE_1T_CLOCK            64
> +#define RTASE_1T_POWER            10000000
> +#define RTASE_IDLESLOPE_INT_SHIFT 25
> +#define RTASE_IDLESLOPE_INT_MASK  GENMASK(31, 25)
> +
> +#define IVEC_NAME_SIZE (IFNAMSIZ + 10)
> +
> +struct rtase_int_vector {
> +	struct rtase_private *tp;
> +	unsigned int irq;
> +	u8 status;
> +	char name[IVEC_NAME_SIZE];
> +	u16 index;
> +	u16 imr_addr;
> +	u16 isr_addr;
> +	u32 imr;
> +	struct list_head ring_list;
> +	struct napi_struct napi;
> +	int (*poll)(struct napi_struct *napi, int budget);
> +};
> +
> +struct rtase_ring {
> +	struct rtase_int_vector *ivec;
> +	void *desc;
> +	dma_addr_t phy_addr;
> +	u32 cur_idx;
> +	u32 dirty_idx;
> +	u16 index;
> +
> +	struct sk_buff *skbuff[NUM_DESC];
> +	union {
> +		u32 len[NUM_DESC];
> +		dma_addr_t data_phy_addr[NUM_DESC];
> +	} mis;
> +
> +	struct list_head ring_entry;
> +	int (*ring_handler)(struct rtase_ring *ring, int budget);
> +};
> +
> +struct rtase_txqos {
> +	int hicredit;
> +	int locredit;
> +	int idleslope;
> +	int sendslope;
> +};
> +
> +struct rtase_private {
> +	void __iomem *mmio_addr;
> +	u32 sw_flag;
> +	u32 mc_filter[2];
> +
> +	struct pci_dev *pdev;
> +	struct net_device *dev;
> +	u32 rx_buf_sz;
> +
> +	struct page_pool *page_pool;
> +	struct rtase_ring tx_ring[RTASE_NUM_TX_QUEUE];
> +	struct rtase_txqos tx_qos[RTASE_NUM_TX_QUEUE];
> +	struct rtase_ring rx_ring[RTASE_NUM_RX_QUEUE];
> +	struct rtase_counters *tally_vaddr;
> +	dma_addr_t tally_paddr;
> +
> +	u32 vlan_filter_ctrl;
> +	u16 vlan_filter_vid[RTASE_VLAN_FILTER_ENTRY_NUM];
> +
> +	struct delayed_work task;

I don't see this member anywhere used. What is it good for?
And related: Why the delayed version?

> +	u8 org_mac_addr[ETH_ALEN];

How's the org_mac_addr different from perm_addr?

> +	struct msix_entry msix_entry[RTASE_NUM_MSIX];
> +	struct rtase_int_vector int_vector[RTASE_NUM_MSIX];
> +
> +	u16 tx_queue_ctrl;
> +	u16 func_tx_queue_num;
> +	u16 func_rx_queue_num;
> +	u16 int_nums;
> +	u16 tx_int_mit;
> +	u16 rx_int_mit;
> +};
> +
> +#define LSO_64K 64000
> +
> +#define NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4)
> +
> +#define TCPHO_MASK GENMASK(24, 18)
> +
> +#define MSS_MAX  0x07FF /* MSS value */
> +#define MSS_MASK GENMASK(28, 18)
> +
> +#endif /* _RTASE_H_ */


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function
  2023-11-15 13:34 ` [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function Justin Lai
@ 2023-11-15 15:02   ` Heiner Kallweit
  2023-11-23  3:16     ` Justin Lai
  0 siblings, 1 reply; 28+ messages in thread
From: Heiner Kallweit @ 2023-11-15 15:02 UTC (permalink / raw)
  To: Justin Lai, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu

On 15.11.2023 14:34, Justin Lai wrote:
> Implement the ethtool function to support users to obtain network card
> information, including obtaining various device settings, Report whether
> physical link is up, Report pause parameters, Set pause parameters,
> Return a set of strings that describe the requested objects, Get number
> of strings that @get_strings will write, Return extended statistics
> about the device.
>
> Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> ---
>  .../net/ethernet/realtek/rtase/rtase_main.c   | 144 ++++++++++++++++++
>  1 file changed, 144 insertions(+)
>
> diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> index b7679b74cc8a..5ea4d51fcc47 100644
> --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> @@ -1900,9 +1900,153 @@ static void rtase_get_mac_address(struct net_device *dev)
>  	ether_addr_copy(dev->perm_addr, dev->dev_addr);
>  }
>
> +static void rtase_get_drvinfo(struct net_device *dev,
> +			      struct ethtool_drvinfo *drvinfo)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
> +	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> +}
> +
> +static int rtase_get_settings(struct net_device *dev,
> +			      struct ethtool_link_ksettings *cmd)
> +{
> +	u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
> +
> +	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
> +						supported);
> +	cmd->base.speed = SPEED_5000;
> +	cmd->base.duplex = DUPLEX_FULL;
> +	cmd->base.port = PORT_MII;
> +	cmd->base.autoneg = AUTONEG_DISABLE;
> +
What are you reporting here? Does this refer to the link between MAC and
switch CPU port? Because I would assume that the switch ports can do autoneg.

> +	return 0;
> +}
> +
> +static void rtase_get_pauseparam(struct net_device *dev,
> +				 struct ethtool_pauseparam *pause)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> +
> +	pause->autoneg = AUTONEG_DISABLE;
> +
> +	if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
> +	    (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
> +		pause->rx_pause = 1;
> +		pause->tx_pause = 1;
> +	} else if ((value & FORCE_TXFLOW_EN)) {
> +		pause->tx_pause = 1;
> +	} else if ((value & FORCE_RXFLOW_EN)) {
> +		pause->rx_pause = 1;
> +	}
> +}
> +
> +static int rtase_set_pauseparam(struct net_device *dev,
> +				struct ethtool_pauseparam *pause)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> +
> +	if (pause->autoneg)
> +		return -EOPNOTSUPP;
> +
> +	value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> +
> +	if (pause->tx_pause)
> +		value |= FORCE_TXFLOW_EN;
> +
> +	if (pause->rx_pause)
> +		value |= FORCE_RXFLOW_EN;
> +
> +	rtase_w16(tp, RTASE_CPLUS_CMD, value);
> +	return 0;
> +}
> +
> +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> +	"tx_packets",
> +	"rx_packets",
> +	"tx_errors",
> +	"rx_errors",
> +	"rx_missed",
> +	"align_errors",
> +	"tx_single_collisions",
> +	"tx_multi_collisions",
> +	"unicast",
> +	"broadcast",
> +	"multicast",
> +	"tx_aborted",
> +	"tx_underrun",
> +};
> +
> +static void rtase_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> +	switch (stringset) {
> +	case ETH_SS_STATS:
> +		memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
> +		break;
> +	}
> +}
> +
> +static int rtase_get_sset_count(struct net_device *dev, int sset)
> +{
> +	int ret = -EOPNOTSUPP;
> +
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		ret = ARRAY_SIZE(rtase_gstrings);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void rtase_get_ethtool_stats(struct net_device *dev,
> +				    struct ethtool_stats *stats, u64 *data)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters;
> +
> +	ASSERT_RTNL();
> +
> +	counters = tp->tally_vaddr;
> +	if (!counters)
> +		return;
> +
> +	rtase_dump_tally_counter(tp);
> +
> +	data[0] = le64_to_cpu(counters->tx_packets);
> +	data[1] = le64_to_cpu(counters->rx_packets);
> +	data[2] = le64_to_cpu(counters->tx_errors);
> +	data[3] = le32_to_cpu(counters->rx_errors);
> +	data[4] = le16_to_cpu(counters->rx_missed);
> +	data[5] = le16_to_cpu(counters->align_errors);
> +	data[6] = le32_to_cpu(counters->tx_one_collision);
> +	data[7] = le32_to_cpu(counters->tx_multi_collision);
> +	data[8] = le64_to_cpu(counters->rx_unicast);
> +	data[9] = le64_to_cpu(counters->rx_broadcast);
> +	data[10] = le32_to_cpu(counters->rx_multicast);
> +	data[11] = le16_to_cpu(counters->tx_aborted);
> +	data[12] = le16_to_cpu(counters->tx_underun);
> +}
> +
> +static const struct ethtool_ops rtase_ethtool_ops = {
> +	.get_drvinfo = rtase_get_drvinfo,
> +	.get_link = ethtool_op_get_link,
> +	.get_link_ksettings = rtase_get_settings,
> +	.get_pauseparam = rtase_get_pauseparam,
> +	.set_pauseparam = rtase_set_pauseparam,
> +	.get_strings = rtase_get_strings,
> +	.get_sset_count = rtase_get_sset_count,
> +	.get_ethtool_stats = rtase_get_ethtool_stats,
> +	.get_ts_info = ethtool_op_get_ts_info,
> +};
> +
>  static void rtase_init_netdev_ops(struct net_device *dev)
>  {
>  	dev->netdev_ops = &rtase_netdev_ops;
> +	dev->ethtool_ops = &rtase_ethtool_ops;
>  }
>
>  static void rtase_reset_interrupt(struct pci_dev *pdev,


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function
  2023-11-15 13:34 ` [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function Justin Lai
@ 2023-11-15 15:02   ` Heiner Kallweit
  2023-11-21  9:30     ` Justin Lai
  0 siblings, 1 reply; 28+ messages in thread
From: Heiner Kallweit @ 2023-11-15 15:02 UTC (permalink / raw)
  To: Justin Lai, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu

On 15.11.2023 14:34, Justin Lai wrote:
> Implement the pci_driver suspend function to enable the device
> to sleep, and implement the resume function to enable the device
> to resume operation.
>
> Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> ---
>  .../net/ethernet/realtek/rtase/rtase_main.c   | 64 +++++++++++++++++++
>  1 file changed, 64 insertions(+)
>
> diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> index 12607663dd72..b7679b74cc8a 100644
> --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> @@ -2323,12 +2323,76 @@ static void rtase_shutdown(struct pci_dev *pdev)
>  	rtase_reset_interrupt(pdev, tp);
>  }
>
> +#ifdef CONFIG_PM
> +static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +
> +	if (!netif_running(dev))
> +		goto out;
> +
> +	netif_stop_queue(dev);
> +	netif_carrier_off(dev);
> +	netif_device_detach(dev);
> +	rtase_hw_reset(dev);
> +
> +out:
> +	pci_save_state(pdev);
> +
> +	return 0;
> +}
> +
> +static int rtase_resume(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct rtase_private *tp = netdev_priv(dev);
> +	int ret;
> +
> +	pci_set_power_state(pdev, PCI_D0);
> +	pci_restore_state(pdev);
> +	pci_enable_wake(pdev, PCI_D0, 0);
> +

IMO this is done by the PCI core already. See other drivers
and PCI core code.

> +	/* restore last modified mac address */
> +	rtase_rar_set(tp, dev->dev_addr);
> +
> +	if (!netif_running(dev))
> +		goto out;
> +
> +	rtase_wait_for_quiescence(dev);
> +
> +	rtase_tx_clear(tp);
> +	rtase_rx_clear(tp);
> +
> +	ret = rtase_init_ring(dev);
> +	if (ret) {
> +		netdev_err(dev, "unable to init ring\n");
> +		rtase_free_desc(tp);
> +		return -ENOMEM;
> +	}
> +
> +	rtase_hw_config(dev);
> +	/* always link, so start to transmit & receive */
> +	rtase_hw_start(dev);
> +
> +	netif_carrier_on(dev);
> +	netif_wake_queue(dev);
> +	netif_device_attach(dev);
> +
> +out:
> +	return 0;
> +}
> +#endif /* CONFIG_PM */
> +
>  static struct pci_driver rtase_pci_driver = {
>  	.name = KBUILD_MODNAME,
>  	.id_table = rtase_pci_tbl,
>  	.probe = rtase_init_one,
>  	.remove = rtase_remove_one,
>  	.shutdown = rtase_shutdown,
> +#ifdef CONFIG_PM
> +	.suspend = rtase_suspend,
> +	.resume = rtase_resume,

Use pm_sleep_ptr and related macros then you don't need the
conditional compiling.

> +#endif
>  };
>
>  module_pci_driver(rtase_pci_driver);


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
  2023-11-15 13:34 ` [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder Justin Lai
@ 2023-11-15 19:58   ` kernel test robot
  2023-11-21 10:00   ` kernel test robot
  1 sibling, 0 replies; 28+ messages in thread
From: kernel test robot @ 2023-11-15 19:58 UTC (permalink / raw)
  To: Justin Lai, kuba
  Cc: oe-kbuild-all, davem, edumazet, pabeni, linux-kernel, netdev,
	andrew, pkshih, larry.chiu, Justin Lai

Hi Justin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Justin-Lai/net-ethernet-realtek-rtase-Add-pci-table-supported-in-this-module/20231115-213811
base:   net-next/main
patch link:    https://lore.kernel.org/r/20231115133414.1221480-13-justinlai0215%40realtek.com
patch subject: [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
config: i386-allmodconfig (https://download.01.org/0day-ci/archive/20231116/202311160332.4oEPIsQA-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231116/202311160332.4oEPIsQA-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311160332.4oEPIsQA-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/net/ethernet/realtek/rtase/rtase_main.c: In function 'rtase_dump_state':
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:1586:44: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
    1586 |         netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
         |                                         ~~~^     ~~~~~~~~~~~~~~
         |                                            |         |
         |                                            |         dma_addr_t {aka unsigned int}
         |                                            long long unsigned int
         |                                         %x
   drivers/net/ethernet/realtek/rtase/rtase_main.c:1592:44: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
    1592 |         netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
         |                                         ~~~^     ~~~~~~~~~~~~~~
         |                                            |         |
         |                                            |         dma_addr_t {aka unsigned int}
         |                                            long long unsigned int
         |                                         %x


vim +1586 drivers/net/ethernet/realtek/rtase/rtase_main.c

ee13d5d822aea2 Justin Lai 2023-11-15  1572  
ee13d5d822aea2 Justin Lai 2023-11-15  1573  static void rtase_dump_state(const struct net_device *dev)
ee13d5d822aea2 Justin Lai 2023-11-15  1574  {
ee13d5d822aea2 Justin Lai 2023-11-15  1575  	const struct rtase_private *tp = netdev_priv(dev);
ee13d5d822aea2 Justin Lai 2023-11-15  1576  	const struct rtase_counters *counters;
ee13d5d822aea2 Justin Lai 2023-11-15  1577  	int max_reg_size = RTASE_PCI_REGS_SIZE;
ee13d5d822aea2 Justin Lai 2023-11-15  1578  	const struct rtase_ring *ring;
ee13d5d822aea2 Justin Lai 2023-11-15  1579  	u32 dword_rd;
ee13d5d822aea2 Justin Lai 2023-11-15  1580  	int n = 0;
ee13d5d822aea2 Justin Lai 2023-11-15  1581  
ee13d5d822aea2 Justin Lai 2023-11-15  1582  	ring = &tp->tx_ring[0];
ee13d5d822aea2 Justin Lai 2023-11-15  1583  	netdev_err(dev, "Tx descriptor info:\n");
ee13d5d822aea2 Justin Lai 2023-11-15  1584  	netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
ee13d5d822aea2 Justin Lai 2023-11-15  1585  	netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
ee13d5d822aea2 Justin Lai 2023-11-15 @1586  	netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
ee13d5d822aea2 Justin Lai 2023-11-15  1587  
ee13d5d822aea2 Justin Lai 2023-11-15  1588  	ring = &tp->rx_ring[0];
ee13d5d822aea2 Justin Lai 2023-11-15  1589  	netdev_err(dev, "Rx descriptor info:\n");
ee13d5d822aea2 Justin Lai 2023-11-15  1590  	netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
ee13d5d822aea2 Justin Lai 2023-11-15  1591  	netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
ee13d5d822aea2 Justin Lai 2023-11-15  1592  	netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
ee13d5d822aea2 Justin Lai 2023-11-15  1593  
ee13d5d822aea2 Justin Lai 2023-11-15  1594  	netdev_err(dev, "Device Registers:\n");
ee13d5d822aea2 Justin Lai 2023-11-15  1595  	netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp, RTASE_CHIP_CMD));
ee13d5d822aea2 Justin Lai 2023-11-15  1596  	netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
ee13d5d822aea2 Justin Lai 2023-11-15  1597  	netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
ee13d5d822aea2 Justin Lai 2023-11-15  1598  	netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1599  		   rtase_r16(tp, RTASE_BOOT_CTL));
ee13d5d822aea2 Justin Lai 2023-11-15  1600  	netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1601  		   rtase_r16(tp, RTASE_EPHY_ISR));
ee13d5d822aea2 Justin Lai 2023-11-15  1602  	netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1603  		   rtase_r16(tp, RTASE_EPHY_IMR));
ee13d5d822aea2 Justin Lai 2023-11-15  1604  	netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1605  		   rtase_r16(tp, RTASE_CLKSW_SET));
ee13d5d822aea2 Justin Lai 2023-11-15  1606  
ee13d5d822aea2 Justin Lai 2023-11-15  1607  	netdev_err(dev, "Dump PCI Registers:\n");
ee13d5d822aea2 Justin Lai 2023-11-15  1608  
ee13d5d822aea2 Justin Lai 2023-11-15  1609  	while (n < max_reg_size) {
ee13d5d822aea2 Justin Lai 2023-11-15  1610  		if ((n % RTASE_DWORD_MOD) == 0)
ee13d5d822aea2 Justin Lai 2023-11-15  1611  			netdev_err(tp->dev, "0x%03x:\n", n);
ee13d5d822aea2 Justin Lai 2023-11-15  1612  
ee13d5d822aea2 Justin Lai 2023-11-15  1613  		pci_read_config_dword(tp->pdev, n, &dword_rd);
ee13d5d822aea2 Justin Lai 2023-11-15  1614  		netdev_err(tp->dev, "%08x\n", dword_rd);
ee13d5d822aea2 Justin Lai 2023-11-15  1615  		n += 4;
ee13d5d822aea2 Justin Lai 2023-11-15  1616  	}
ee13d5d822aea2 Justin Lai 2023-11-15  1617  
ee13d5d822aea2 Justin Lai 2023-11-15  1618  	netdev_err(dev, "Dump tally counter:\n");
ee13d5d822aea2 Justin Lai 2023-11-15  1619  	counters = tp->tally_vaddr;
ee13d5d822aea2 Justin Lai 2023-11-15  1620  	rtase_dump_tally_counter(tp);
ee13d5d822aea2 Justin Lai 2023-11-15  1621  
ee13d5d822aea2 Justin Lai 2023-11-15  1622  	netdev_err(dev, "tx_packets %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1623  		   le64_to_cpu(counters->tx_packets));
ee13d5d822aea2 Justin Lai 2023-11-15  1624  	netdev_err(dev, "rx_packets %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1625  		   le64_to_cpu(counters->rx_packets));
ee13d5d822aea2 Justin Lai 2023-11-15  1626  	netdev_err(dev, "tx_errors %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1627  		   le64_to_cpu(counters->tx_errors));
ee13d5d822aea2 Justin Lai 2023-11-15  1628  	netdev_err(dev, "rx_missed %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1629  		   le64_to_cpu(counters->rx_missed));
ee13d5d822aea2 Justin Lai 2023-11-15  1630  	netdev_err(dev, "align_errors %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1631  		   le64_to_cpu(counters->align_errors));
ee13d5d822aea2 Justin Lai 2023-11-15  1632  	netdev_err(dev, "tx_one_collision %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1633  		   le64_to_cpu(counters->tx_one_collision));
ee13d5d822aea2 Justin Lai 2023-11-15  1634  	netdev_err(dev, "tx_multi_collision %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1635  		   le64_to_cpu(counters->tx_multi_collision));
ee13d5d822aea2 Justin Lai 2023-11-15  1636  	netdev_err(dev, "rx_unicast %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1637  		   le64_to_cpu(counters->rx_unicast));
ee13d5d822aea2 Justin Lai 2023-11-15  1638  	netdev_err(dev, "rx_broadcast %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1639  		   le64_to_cpu(counters->rx_broadcast));
ee13d5d822aea2 Justin Lai 2023-11-15  1640  	netdev_err(dev, "rx_multicast %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1641  		   le64_to_cpu(counters->rx_multicast));
ee13d5d822aea2 Justin Lai 2023-11-15  1642  	netdev_err(dev, "tx_aborted %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1643  		   le64_to_cpu(counters->tx_aborted));
ee13d5d822aea2 Justin Lai 2023-11-15  1644  	netdev_err(dev, "tx_underun %lld\n",
ee13d5d822aea2 Justin Lai 2023-11-15  1645  		   le64_to_cpu(counters->tx_underun));
ee13d5d822aea2 Justin Lai 2023-11-15  1646  }
ee13d5d822aea2 Justin Lai 2023-11-15  1647  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver
  2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
                   ` (12 preceding siblings ...)
  2023-11-15 13:34 ` [PATCH net-next v11 13/13] MAINTAINERS: Add the rtase ethernet driver entry Justin Lai
@ 2023-11-16 17:57 ` Simon Horman
  2023-11-21  9:35   ` Justin Lai
  13 siblings, 1 reply; 28+ messages in thread
From: Simon Horman @ 2023-11-16 17:57 UTC (permalink / raw)
  To: Justin Lai
  Cc: kuba, davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	pkshih, larry.chiu

On Wed, Nov 15, 2023 at 09:34:01PM +0800, Justin Lai wrote:
> This series includes adding realtek automotive ethernet driver 
> and adding rtase ethernet driver entry in MAINTAINERS file.
> 
> This ethernet device driver for the PCIe interface of 
> Realtek Automotive Ethernet Switch,applicable to 
> RTL9054, RTL9068, RTL9072, RTL9075, RTL9068, RTL9071.

...

>  MAINTAINERS                                   |    7 +
>  drivers/net/ethernet/realtek/Kconfig          |   17 +
>  drivers/net/ethernet/realtek/Makefile         |    1 +
>  drivers/net/ethernet/realtek/rtase/Makefile   |   10 +
>  drivers/net/ethernet/realtek/rtase/rtase.h    |  353 +++
>  .../net/ethernet/realtek/rtase/rtase_main.c   | 2542 +++++++++++++++++
>  drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
>  drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++

Hi Justin,

Unless I am mistaken, by the end of this patch set tt.c is identical to
rtase_main.c, and tt.h is identical to rtase.h.

If so, why?

$ cd drivers/net/ethernet/realtek/rtase
$ sha1sum *.[ch] | sort
9762c7f0fc1acb214d1705905495fad6b902a3c8  rtase.h
9762c7f0fc1acb214d1705905495fad6b902a3c8  tt.h
ccfe889f5ae3b6ecfa0dfba91d30fd7beffd4291  rtase_main.c
ccfe889f5ae3b6ecfa0dfba91d30fd7beffd4291  tt.c


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
  2023-11-15 15:02   ` Heiner Kallweit
@ 2023-11-16 18:06   ` Simon Horman
  2023-11-23  3:48     ` Justin Lai
  2023-11-18 22:50   ` Jakub Kicinski
  2 siblings, 1 reply; 28+ messages in thread
From: Simon Horman @ 2023-11-16 18:06 UTC (permalink / raw)
  To: Justin Lai
  Cc: kuba, davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	pkshih, larry.chiu

On Wed, Nov 15, 2023 at 09:34:06PM +0800, Justin Lai wrote:
> Implement rtase_hw_config to set default hardware settings, including
> setting interrupt mitigation, tx/rx DMA burst, interframe gap time,
> rx packet filter, near fifo threshold and fill descriptor ring and
> tally counter address, and enable flow control. When filling the
> rx descriptor ring, the first group of queues needs to be processed
> separately because the positions of the first group of queues are not
> regular with other subsequent groups. The other queues are all newly
> added features, but we want to retain the original design. So they were
> not put together.
> 
> Signed-off-by: Justin Lai <justinlai0215@realtek.com>

Hi Justin,

some minor feedback from my side.

* I think that "rtase: " would be a more appropriate prefix
  for the patches in this patch-set.

  Subject: [PATCH net-next vX m/n] rtase: ...

...

> diff --git a/drivers/net/ethernet/realtek/rtase/tt.c b/drivers/net/ethernet/realtek/rtase/tt.c

...

> new file mode 100644
> index 000000000000..5ea4d51fcc47
> --- /dev/null
> +++ b/drivers/net/ethernet/realtek/rtase/tt.c

...

> +static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n)
> +{
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	struct net_device *dev = tp->dev;
> +	struct tx_desc *desc_base = ring->desc;
> +	u32 i;

nit: Please consider using reverse xmas tree - longest line to shortest -
     for new Networking code.

...

> +static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
> +{
> +	u8 ip_protocol;
> +	u32 csum_cmd;
> +
> +	switch (vlan_get_protocol(skb)) {
> +	case htons(ETH_P_IP):
> +		csum_cmd = TX_IPCS_C;
> +		ip_protocol = ip_hdr(skb)->protocol;
> +		break;
> +
> +	case htons(ETH_P_IPV6):
> +		csum_cmd = TX_IPV6F_C;
> +		ip_protocol = ipv6_hdr(skb)->nexthdr;
> +		break;
> +
> +	default:
> +		ip_protocol = IPPROTO_RAW;
> +		break;

If the default branch is taken in this switch statement,
then csum_cmd is used uninitialised below.

As flagged by a clang-16 W=1 build

> +	}
> +
> +	if (ip_protocol == IPPROTO_TCP)
> +		csum_cmd |= TX_TCPCS_C;
> +	else if (ip_protocol == IPPROTO_UDP)
> +		csum_cmd |= TX_UDPCS_C;
> +	else
> +		WARN_ON_ONCE(1);
> +
> +	csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
> +
> +	return csum_cmd;
> +}
> +
> +static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
> +			    u32 opts1, u32 opts2)
> +{
> +	const struct skb_shared_info *info = skb_shinfo(skb);
> +	const struct rtase_private *tp = ring->ivec->tp;
> +	const u8 nr_frags = info->nr_frags;
> +	struct tx_desc *txd = NULL;
> +	u32 cur_frag, entry;
> +	u64 pkt_len_cnt = 0;

pkt_len_cnt is initialised here...

> +
> +	entry = ring->cur_idx;
> +	for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
> +		const skb_frag_t *frag = &info->frags[cur_frag];
> +		dma_addr_t mapping;
> +		u32 status, len;
> +		void *addr;
> +
> +		entry = (entry + 1) % NUM_DESC;
> +
> +		txd = ring->desc + sizeof(struct tx_desc) * entry;
> +		len = skb_frag_size(frag);
> +		addr = skb_frag_address(frag);
> +		mapping = dma_map_single(&tp->pdev->dev, addr, len,
> +					 DMA_TO_DEVICE);
> +
> +		if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
> +			if (unlikely(net_ratelimit()))
> +				netdev_err(tp->dev,
> +					   "Failed to map TX fragments DMA!\n");
> +
> +			goto err_out;
> +		}
> +
> +		if (((entry + 1) % NUM_DESC) == 0)
> +			status = (opts1 | len | RING_END);
> +		else
> +			status = opts1 | len;
> +
> +		if (cur_frag == (nr_frags - 1)) {
> +			ring->skbuff[entry] = skb;
> +			status |= TX_LAST_FRAG;
> +		}
> +
> +		ring->mis.len[entry] = len;
> +		txd->addr = cpu_to_le64(mapping);
> +		txd->opts2 = cpu_to_le32(opts2);
> +
> +		/* make sure the operating fields have been updated */
> +		wmb();
> +		txd->opts1 = cpu_to_le32(status);
> +		pkt_len_cnt += len;

... and incremented here. But is otherwise unused.

As flagged by a clang-16 W=1 build.

> +	}
> +
> +	return cur_frag;
> +
> +err_out:
> +	rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
> +	return -EIO;
> +}

...

> +static void rtase_dump_state(const struct net_device *dev)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters;
> +	int max_reg_size = RTASE_PCI_REGS_SIZE;
> +	const struct rtase_ring *ring;
> +	u32 dword_rd;
> +	int n = 0;
> +
> +	ring = &tp->tx_ring[0];
> +	netdev_err(dev, "Tx descriptor info:\n");
> +	netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
> +	netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
> +	netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
> +
> +	ring = &tp->rx_ring[0];
> +	netdev_err(dev, "Rx descriptor info:\n");
> +	netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
> +	netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
> +	netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
> +
> +	netdev_err(dev, "Device Registers:\n");
> +	netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp, RTASE_CHIP_CMD));
> +	netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
> +	netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
> +	netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
> +		   rtase_r16(tp, RTASE_BOOT_CTL));
> +	netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
> +		   rtase_r16(tp, RTASE_EPHY_ISR));
> +	netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
> +		   rtase_r16(tp, RTASE_EPHY_IMR));
> +	netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
> +		   rtase_r16(tp, RTASE_CLKSW_SET));
> +
> +	netdev_err(dev, "Dump PCI Registers:\n");
> +
> +	while (n < max_reg_size) {
> +		if ((n % RTASE_DWORD_MOD) == 0)
> +			netdev_err(tp->dev, "0x%03x:\n", n);
> +
> +		pci_read_config_dword(tp->pdev, n, &dword_rd);
> +		netdev_err(tp->dev, "%08x\n", dword_rd);
> +		n += 4;
> +	}
> +
> +	netdev_err(dev, "Dump tally counter:\n");
> +	counters = tp->tally_vaddr;
> +	rtase_dump_tally_counter(tp);
> +
> +	netdev_err(dev, "tx_packets %lld\n",
> +		   le64_to_cpu(counters->tx_packets));

tx_packets is __le16 not __le64, so I think you want:

	netdev_err(dev, "rx_missed %d\n",
		   le16_to_cpu(counters->rx_missed));

Likewise for align_errors, tx_aborted, and tx_underun.

As flagged by Sparse

> +	netdev_err(dev, "rx_packets %lld\n",
> +		   le64_to_cpu(counters->rx_packets));
> +	netdev_err(dev, "tx_errors %lld\n",
> +		   le64_to_cpu(counters->tx_errors));
> +	netdev_err(dev, "rx_missed %lld\n",
> +		   le64_to_cpu(counters->rx_missed));
> +	netdev_err(dev, "align_errors %lld\n",
> +		   le64_to_cpu(counters->align_errors));
> +	netdev_err(dev, "tx_one_collision %lld\n",
> +		   le64_to_cpu(counters->tx_one_collision));

Similarly, tx_one_collision is __le32 not __le64, so I think you want:
.
	netdev_err(dev, "tx_one_collision %d\n",
		   le32_to_cpu(counters->tx_one_collision));

Likewise for tx_multi_collision, and rx_multicast.

There area also similar problems in rtase_main.c:rtase_dump_state(),
added elsewhere in this patch-set.

> +	netdev_err(dev, "tx_multi_collision %lld\n",
> +		   le64_to_cpu(counters->tx_multi_collision));
> +	netdev_err(dev, "rx_unicast %lld\n",
> +		   le64_to_cpu(counters->rx_unicast));
> +	netdev_err(dev, "rx_broadcast %lld\n",
> +		   le64_to_cpu(counters->rx_broadcast));
> +	netdev_err(dev, "rx_multicast %lld\n",
> +		   le64_to_cpu(counters->rx_multicast));
> +	netdev_err(dev, "tx_aborted %lld\n",
> +		   le64_to_cpu(counters->tx_aborted));
> +	netdev_err(dev, "tx_underun %lld\n",
> +		   le64_to_cpu(counters->tx_underun));
> +}

...

> +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> +	"tx_packets",
> +	"rx_packets",
> +	"tx_errors",
> +	"rx_errors",
> +	"rx_missed",
> +	"align_errors",
> +	"tx_single_collisions",
> +	"tx_multi_collisions",
> +	"unicast",
> +	"broadcast",
> +	"multicast",
> +	"tx_aborted",
> +	"tx_underrun",
> +};
> +
> +static void rtase_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> +	switch (stringset) {
> +	case ETH_SS_STATS:
> +		memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));

Compilers don't think this is seem correct, because *rtase_gsrings is the
first element of that array, rather than the entire array. And thus
is only ETH_GSTRING_LEN bytes long, rather than n * ETH_GSTRING_LEN bytes
long.

I think what you want is (compile tested only!):

		memcpy(data, rtase_gstrings, sizeof(rtase_gstrings));

Flagged by a clang-16 W=1 build.

> +		break;
> +	}
> +}

...

> +static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out,
> +			    void __iomem **ioaddr_out)
> +{
> +	struct net_device *dev;
> +	void __iomem *ioaddr;
> +	int ret = -ENOMEM;
> +
> +	/* dev zeroed in alloc_etherdev */
> +	dev = alloc_etherdev_mq(sizeof(struct rtase_private),
> +				RTASE_FUNC_TXQ_NUM);
> +	if (!dev)
> +		goto err_out;
> +
> +	SET_NETDEV_DEV(dev, &pdev->dev);
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret < 0)
> +		goto err_out_free_dev;
> +
> +	/* make sure PCI base addr 1 is MMIO */
> +	if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> +		ret = -ENODEV;
> +		goto err_out_disable;
> +	}
> +
> +	/* check for weird/broken PCI region reporting */
> +	if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
> +		ret = -ENODEV;
> +		goto err_out_disable;
> +	}
> +
> +	ret = pci_request_regions(pdev, KBUILD_MODNAME);
> +	if (ret < 0)
> +		goto err_out_disable;
> +
> +	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
> +		dev->features |= NETIF_F_HIGHDMA;
> +	else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
> +		goto err_out_free_res;
> +	else
> +		dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
> +
> +	pci_set_master(pdev);
> +
> +	/* ioremap MMIO region */
> +	ioaddr = ioremap(pci_resource_start(pdev, 2),
> +			 pci_resource_len(pdev, 2));
> +	if (!ioaddr) {
> +		ret = -EIO;
> +		goto err_out_free_res;
> +	}
> +
> +	*ioaddr_out = ioaddr;
> +	*dev_out = dev;
> +	goto out;

nit: I think it's slightly more idiomatic to simply return 0 here,
     and drop the out label below.

> +
> +err_out_free_res:
> +	pci_release_regions(pdev);
> +
> +err_out_disable:
> +	pci_disable_device(pdev);
> +
> +err_out_free_dev:
> +	free_netdev(dev);
> +
> +err_out:
> +	*ioaddr_out = NULL;
> +	*dev_out = NULL;
> +
> +out:
> +	return ret;
> +}

...

> diff --git a/drivers/net/ethernet/realtek/rtase/tt.h b/drivers/net/ethernet/realtek/rtase/tt.h
> new file mode 100644
> index 000000000000..9239c518c504
> --- /dev/null
> +++ b/drivers/net/ethernet/realtek/rtase/tt.h
> @@ -0,0 +1,353 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/*
> + *  rtase is the Linux device driver released for Realtek Automotive Switch
> + *  controllers with PCI-Express interface.
> + *
> + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> + */
> +
> +#ifndef _RTASE_H_
> +#define _RTASE_H_

If the code in tt.h is only used in tt.c, then perhaps the
code can simply be moved into tt.c.

...

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
  2023-11-15 15:02   ` Heiner Kallweit
  2023-11-16 18:06   ` Simon Horman
@ 2023-11-18 22:50   ` Jakub Kicinski
  2023-11-20 13:22     ` Justin Lai
  2 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2023-11-18 22:50 UTC (permalink / raw)
  To: Justin Lai
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew, pkshih,
	larry.chiu

On Wed, 15 Nov 2023 21:34:06 +0800 Justin Lai wrote:
> +	.ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +	.ndo_poll_controller = rtase_netpoll,
> +#endif
> +	.ndo_setup_tc = rtase_setup_tc,

This patch is still way too huge. Please remove more functionality 
from the initial version of the driver. You certainly don't need VLAN
support or CBS offload to pass packets.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-18 22:50   ` Jakub Kicinski
@ 2023-11-20 13:22     ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-20 13:22 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On Wed, 15 Nov 2023 21:34:06 +0800 Justin Lai wrote:
> > +     .ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
> > +     .ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid, #ifdef
> > +CONFIG_NET_POLL_CONTROLLER
> > +     .ndo_poll_controller = rtase_netpoll, #endif
> > +     .ndo_setup_tc = rtase_setup_tc,
> 
> This patch is still way too huge. Please remove more functionality from the
> initial version of the driver. You certainly don't need VLAN support or CBS
> offload to pass packets.

Thanks for your review, I will remove some less necessary functions and make the patch smaller.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function
  2023-11-15 15:02   ` Heiner Kallweit
@ 2023-11-21  9:30     ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-21  9:30 UTC (permalink / raw)
  To: Heiner Kallweit, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On 15.11.2023 14:34, Justin Lai wrote:
> > Implement the pci_driver suspend function to enable the device to
> > sleep, and implement the resume function to enable the device to
> > resume operation.
> >
> > Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> > ---
> >  .../net/ethernet/realtek/rtase/rtase_main.c   | 64 +++++++++++++++++++
> >  1 file changed, 64 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > index 12607663dd72..b7679b74cc8a 100644
> > --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > @@ -2323,12 +2323,76 @@ static void rtase_shutdown(struct pci_dev
> *pdev)
> >       rtase_reset_interrupt(pdev, tp);  }
> >
> > +#ifdef CONFIG_PM
> > +static int rtase_suspend(struct pci_dev *pdev, pm_message_t state) {
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +
> > +     if (!netif_running(dev))
> > +             goto out;
> > +
> > +     netif_stop_queue(dev);
> > +     netif_carrier_off(dev);
> > +     netif_device_detach(dev);
> > +     rtase_hw_reset(dev);
> > +
> > +out:
> > +     pci_save_state(pdev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rtase_resume(struct pci_dev *pdev) {
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     int ret;
> > +
> > +     pci_set_power_state(pdev, PCI_D0);
> > +     pci_restore_state(pdev);
> > +     pci_enable_wake(pdev, PCI_D0, 0);
> > +
> 
> IMO this is done by the PCI core already. See other drivers and PCI core code.

Thank you, I will check this part.
> 
> > +     /* restore last modified mac address */
> > +     rtase_rar_set(tp, dev->dev_addr);
> > +
> > +     if (!netif_running(dev))
> > +             goto out;
> > +
> > +     rtase_wait_for_quiescence(dev);
> > +
> > +     rtase_tx_clear(tp);
> > +     rtase_rx_clear(tp);
> > +
> > +     ret = rtase_init_ring(dev);
> > +     if (ret) {
> > +             netdev_err(dev, "unable to init ring\n");
> > +             rtase_free_desc(tp);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     rtase_hw_config(dev);
> > +     /* always link, so start to transmit & receive */
> > +     rtase_hw_start(dev);
> > +
> > +     netif_carrier_on(dev);
> > +     netif_wake_queue(dev);
> > +     netif_device_attach(dev);
> > +
> > +out:
> > +     return 0;
> > +}
> > +#endif /* CONFIG_PM */
> > +
> >  static struct pci_driver rtase_pci_driver = {
> >       .name = KBUILD_MODNAME,
> >       .id_table = rtase_pci_tbl,
> >       .probe = rtase_init_one,
> >       .remove = rtase_remove_one,
> >       .shutdown = rtase_shutdown,
> > +#ifdef CONFIG_PM
> > +     .suspend = rtase_suspend,
> > +     .resume = rtase_resume,
> 
> Use pm_sleep_ptr and related macros then you don't need the conditional
> compiling.

Thanks for your suggestion, I will modify this driver.
> 
> > +#endif
> >  };
> >
> >  module_pci_driver(rtase_pci_driver);


^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver
  2023-11-16 17:57 ` [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Simon Horman
@ 2023-11-21  9:35   ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-21  9:35 UTC (permalink / raw)
  To: Simon Horman
  Cc: kuba, davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On Wed, Nov 15, 2023 at 09:34:01PM +0800, Justin Lai wrote:
> > This series includes adding realtek automotive ethernet driver and
> > adding rtase ethernet driver entry in MAINTAINERS file.
> >
> > This ethernet device driver for the PCIe interface of Realtek
> > Automotive Ethernet Switch,applicable to RTL9054, RTL9068, RTL9072,
> > RTL9075, RTL9068, RTL9071.
> 
> ...
> 
> >  MAINTAINERS                                   |    7 +
> >  drivers/net/ethernet/realtek/Kconfig          |   17 +
> >  drivers/net/ethernet/realtek/Makefile         |    1 +
> >  drivers/net/ethernet/realtek/rtase/Makefile   |   10 +
> >  drivers/net/ethernet/realtek/rtase/rtase.h    |  353 +++
> >  .../net/ethernet/realtek/rtase/rtase_main.c   | 2542 +++++++++++++++++
> >  drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
> >  drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++
> 
> Hi Justin,
> 
> Unless I am mistaken, by the end of this patch set tt.c is identical to
> rtase_main.c, and tt.h is identical to rtase.h.
> 
> If so, why?

Sorry, tt.c and tt.h are redundant codes. This part will be corrected in the next version of the patch.
> 
> $ cd drivers/net/ethernet/realtek/rtase
> $ sha1sum *.[ch] | sort
> 9762c7f0fc1acb214d1705905495fad6b902a3c8  rtase.h
> 9762c7f0fc1acb214d1705905495fad6b902a3c8  tt.h
> ccfe889f5ae3b6ecfa0dfba91d30fd7beffd4291  rtase_main.c
> ccfe889f5ae3b6ecfa0dfba91d30fd7beffd4291  tt.c


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
  2023-11-15 13:34 ` [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder Justin Lai
  2023-11-15 19:58   ` kernel test robot
@ 2023-11-21 10:00   ` kernel test robot
  1 sibling, 0 replies; 28+ messages in thread
From: kernel test robot @ 2023-11-21 10:00 UTC (permalink / raw)
  To: Justin Lai, kuba
  Cc: llvm, oe-kbuild-all, davem, edumazet, pabeni, linux-kernel,
	netdev, andrew, pkshih, larry.chiu, Justin Lai

Hi Justin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Justin-Lai/net-ethernet-realtek-rtase-Add-pci-table-supported-in-this-module/20231115-213811
base:   net-next/main
patch link:    https://lore.kernel.org/r/20231115133414.1221480-13-justinlai0215%40realtek.com
patch subject: [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20231121/202311211750.4FwMt8rx-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231121/202311211750.4FwMt8rx-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311211750.4FwMt8rx-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/net/ethernet/realtek/rtase/rtase_main.c:1243:2: warning: variable 'csum_cmd' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
    1243 |         default:
         |         ^~~~~~~
   drivers/net/ethernet/realtek/rtase/rtase_main.c:1255:2: note: uninitialized use occurs here
    1255 |         csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
         |         ^~~~~~~~
   drivers/net/ethernet/realtek/rtase/rtase_main.c:1230:14: note: initialize the variable 'csum_cmd' to silence this warning
    1230 |         u32 csum_cmd;
         |                     ^
         |                      = 0
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:1268:6: warning: variable 'pkt_len_cnt' set but not used [-Wunused-but-set-variable]
    1268 |         u64 pkt_len_cnt = 0;
         |             ^
   In file included from drivers/net/ethernet/realtek/rtase/rtase_main.c:47:
   In file included from include/linux/delay.h:23:
   In file included from include/linux/sched.h:14:
   In file included from include/linux/pid.h:5:
   In file included from include/linux/rculist.h:11:
   In file included from include/linux/rcupdate.h:26:
   In file included from include/linux/irqflags.h:17:
   In file included from arch/arm64/include/asm/irqflags.h:10:
   In file included from arch/arm64/include/asm/ptrace.h:11:
   In file included from arch/arm64/include/asm/cpufeature.h:26:
   In file included from include/linux/cpumask.h:12:
   In file included from include/linux/bitmap.h:12:
   In file included from include/linux/string.h:295:
   include/linux/fortify-string.h:588:4: warning: call to '__read_overflow2_field' declared with 'warning' attribute: detected read beyond size of field (2nd parameter); maybe use struct_group()? [-Wattribute-warning]
     588 |                         __read_overflow2_field(q_size_field, size);
         |                         ^
   3 warnings generated.


vim +/csum_cmd +1243 drivers/net/ethernet/realtek/rtase/rtase_main.c

7f5e83b995e2f8 Justin Lai 2023-11-15  1226  
7f5e83b995e2f8 Justin Lai 2023-11-15  1227  static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
7f5e83b995e2f8 Justin Lai 2023-11-15  1228  {
7f5e83b995e2f8 Justin Lai 2023-11-15  1229  	u8 ip_protocol;
7f5e83b995e2f8 Justin Lai 2023-11-15  1230  	u32 csum_cmd;
7f5e83b995e2f8 Justin Lai 2023-11-15  1231  
7f5e83b995e2f8 Justin Lai 2023-11-15  1232  	switch (vlan_get_protocol(skb)) {
7f5e83b995e2f8 Justin Lai 2023-11-15  1233  	case htons(ETH_P_IP):
7f5e83b995e2f8 Justin Lai 2023-11-15  1234  		csum_cmd = TX_IPCS_C;
7f5e83b995e2f8 Justin Lai 2023-11-15  1235  		ip_protocol = ip_hdr(skb)->protocol;
7f5e83b995e2f8 Justin Lai 2023-11-15  1236  		break;
7f5e83b995e2f8 Justin Lai 2023-11-15  1237  
7f5e83b995e2f8 Justin Lai 2023-11-15  1238  	case htons(ETH_P_IPV6):
7f5e83b995e2f8 Justin Lai 2023-11-15  1239  		csum_cmd = TX_IPV6F_C;
7f5e83b995e2f8 Justin Lai 2023-11-15  1240  		ip_protocol = ipv6_hdr(skb)->nexthdr;
7f5e83b995e2f8 Justin Lai 2023-11-15  1241  		break;
7f5e83b995e2f8 Justin Lai 2023-11-15  1242  
7f5e83b995e2f8 Justin Lai 2023-11-15 @1243  	default:
7f5e83b995e2f8 Justin Lai 2023-11-15  1244  		ip_protocol = IPPROTO_RAW;
7f5e83b995e2f8 Justin Lai 2023-11-15  1245  		break;
7f5e83b995e2f8 Justin Lai 2023-11-15  1246  	}
7f5e83b995e2f8 Justin Lai 2023-11-15  1247  
7f5e83b995e2f8 Justin Lai 2023-11-15  1248  	if (ip_protocol == IPPROTO_TCP)
7f5e83b995e2f8 Justin Lai 2023-11-15  1249  		csum_cmd |= TX_TCPCS_C;
7f5e83b995e2f8 Justin Lai 2023-11-15  1250  	else if (ip_protocol == IPPROTO_UDP)
7f5e83b995e2f8 Justin Lai 2023-11-15  1251  		csum_cmd |= TX_UDPCS_C;
7f5e83b995e2f8 Justin Lai 2023-11-15  1252  	else
7f5e83b995e2f8 Justin Lai 2023-11-15  1253  		WARN_ON_ONCE(1);
7f5e83b995e2f8 Justin Lai 2023-11-15  1254  
7f5e83b995e2f8 Justin Lai 2023-11-15  1255  	csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
7f5e83b995e2f8 Justin Lai 2023-11-15  1256  
7f5e83b995e2f8 Justin Lai 2023-11-15  1257  	return csum_cmd;
7f5e83b995e2f8 Justin Lai 2023-11-15  1258  }
7f5e83b995e2f8 Justin Lai 2023-11-15  1259  
7f5e83b995e2f8 Justin Lai 2023-11-15  1260  static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
7f5e83b995e2f8 Justin Lai 2023-11-15  1261  			    u32 opts1, u32 opts2)
7f5e83b995e2f8 Justin Lai 2023-11-15  1262  {
7f5e83b995e2f8 Justin Lai 2023-11-15  1263  	const struct skb_shared_info *info = skb_shinfo(skb);
7f5e83b995e2f8 Justin Lai 2023-11-15  1264  	const struct rtase_private *tp = ring->ivec->tp;
7f5e83b995e2f8 Justin Lai 2023-11-15  1265  	const u8 nr_frags = info->nr_frags;
7f5e83b995e2f8 Justin Lai 2023-11-15  1266  	struct tx_desc *txd = NULL;
7f5e83b995e2f8 Justin Lai 2023-11-15  1267  	u32 cur_frag, entry;
7f5e83b995e2f8 Justin Lai 2023-11-15 @1268  	u64 pkt_len_cnt = 0;
7f5e83b995e2f8 Justin Lai 2023-11-15  1269  
7f5e83b995e2f8 Justin Lai 2023-11-15  1270  	entry = ring->cur_idx;
7f5e83b995e2f8 Justin Lai 2023-11-15  1271  	for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
7f5e83b995e2f8 Justin Lai 2023-11-15  1272  		const skb_frag_t *frag = &info->frags[cur_frag];
7f5e83b995e2f8 Justin Lai 2023-11-15  1273  		dma_addr_t mapping;
7f5e83b995e2f8 Justin Lai 2023-11-15  1274  		u32 status, len;
7f5e83b995e2f8 Justin Lai 2023-11-15  1275  		void *addr;
7f5e83b995e2f8 Justin Lai 2023-11-15  1276  
7f5e83b995e2f8 Justin Lai 2023-11-15  1277  		entry = (entry + 1) % NUM_DESC;
7f5e83b995e2f8 Justin Lai 2023-11-15  1278  
7f5e83b995e2f8 Justin Lai 2023-11-15  1279  		txd = ring->desc + sizeof(struct tx_desc) * entry;
7f5e83b995e2f8 Justin Lai 2023-11-15  1280  		len = skb_frag_size(frag);
7f5e83b995e2f8 Justin Lai 2023-11-15  1281  		addr = skb_frag_address(frag);
7f5e83b995e2f8 Justin Lai 2023-11-15  1282  		mapping = dma_map_single(&tp->pdev->dev, addr, len,
7f5e83b995e2f8 Justin Lai 2023-11-15  1283  					 DMA_TO_DEVICE);
7f5e83b995e2f8 Justin Lai 2023-11-15  1284  
7f5e83b995e2f8 Justin Lai 2023-11-15  1285  		if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
7f5e83b995e2f8 Justin Lai 2023-11-15  1286  			if (unlikely(net_ratelimit()))
7f5e83b995e2f8 Justin Lai 2023-11-15  1287  				netdev_err(tp->dev,
7f5e83b995e2f8 Justin Lai 2023-11-15  1288  					   "Failed to map TX fragments DMA!\n");
7f5e83b995e2f8 Justin Lai 2023-11-15  1289  
7f5e83b995e2f8 Justin Lai 2023-11-15  1290  			goto err_out;
7f5e83b995e2f8 Justin Lai 2023-11-15  1291  		}
7f5e83b995e2f8 Justin Lai 2023-11-15  1292  
7f5e83b995e2f8 Justin Lai 2023-11-15  1293  		if (((entry + 1) % NUM_DESC) == 0)
7f5e83b995e2f8 Justin Lai 2023-11-15  1294  			status = (opts1 | len | RING_END);
7f5e83b995e2f8 Justin Lai 2023-11-15  1295  		else
7f5e83b995e2f8 Justin Lai 2023-11-15  1296  			status = opts1 | len;
7f5e83b995e2f8 Justin Lai 2023-11-15  1297  
7f5e83b995e2f8 Justin Lai 2023-11-15  1298  		if (cur_frag == (nr_frags - 1)) {
7f5e83b995e2f8 Justin Lai 2023-11-15  1299  			ring->skbuff[entry] = skb;
7f5e83b995e2f8 Justin Lai 2023-11-15  1300  			status |= TX_LAST_FRAG;
7f5e83b995e2f8 Justin Lai 2023-11-15  1301  		}
7f5e83b995e2f8 Justin Lai 2023-11-15  1302  
7f5e83b995e2f8 Justin Lai 2023-11-15  1303  		ring->mis.len[entry] = len;
7f5e83b995e2f8 Justin Lai 2023-11-15  1304  		txd->addr = cpu_to_le64(mapping);
7f5e83b995e2f8 Justin Lai 2023-11-15  1305  		txd->opts2 = cpu_to_le32(opts2);
7f5e83b995e2f8 Justin Lai 2023-11-15  1306  
7f5e83b995e2f8 Justin Lai 2023-11-15  1307  		/* make sure the operating fields have been updated */
7f5e83b995e2f8 Justin Lai 2023-11-15  1308  		wmb();
7f5e83b995e2f8 Justin Lai 2023-11-15  1309  		txd->opts1 = cpu_to_le32(status);
7f5e83b995e2f8 Justin Lai 2023-11-15  1310  		pkt_len_cnt += len;
7f5e83b995e2f8 Justin Lai 2023-11-15  1311  	}
7f5e83b995e2f8 Justin Lai 2023-11-15  1312  
7f5e83b995e2f8 Justin Lai 2023-11-15  1313  	return cur_frag;
7f5e83b995e2f8 Justin Lai 2023-11-15  1314  
7f5e83b995e2f8 Justin Lai 2023-11-15  1315  err_out:
7f5e83b995e2f8 Justin Lai 2023-11-15  1316  	rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
7f5e83b995e2f8 Justin Lai 2023-11-15  1317  	return -EIO;
7f5e83b995e2f8 Justin Lai 2023-11-15  1318  }
7f5e83b995e2f8 Justin Lai 2023-11-15  1319  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-15 15:02   ` Heiner Kallweit
@ 2023-11-21 11:54     ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-21 11:54 UTC (permalink / raw)
  To: Heiner Kallweit, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On 15.11.2023 14:34, Justin Lai wrote:
> > Implement rtase_hw_config to set default hardware settings, including
> > setting interrupt mitigation, tx/rx DMA burst, interframe gap time,
> > rx packet filter, near fifo threshold and fill descriptor ring and
> > tally counter address, and enable flow control. When filling the
> > rx descriptor ring, the first group of queues needs to be processed
> > separately because the positions of the first group of queues are not
> > regular with other subsequent groups. The other queues are all newly
> > added features, but we want to retain the original design. So they were
> > not put together.
> >
> > Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> > ---
> >  .../net/ethernet/realtek/rtase/rtase_main.c   |  243 ++
> >  drivers/net/ethernet/realtek/rtase/tt.c       | 2542 +++++++++++++++++
> >  drivers/net/ethernet/realtek/rtase/tt.h       |  353 +++
> >  3 files changed, 3138 insertions(+)
> >  create mode 100644 drivers/net/ethernet/realtek/rtase/tt.c
> >  create mode 100644 drivers/net/ethernet/realtek/rtase/tt.h
> >
> > diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > index 46d128a68844..4e04189050cc 100644
> > --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > @@ -466,6 +466,25 @@ static int rtase_init_ring(const struct net_device
> *dev)
> >       return -ENOMEM;
> >  }
> >
> > +static void rtase_interrupt_mitigation(const struct rtase_private *tp)
> > +{
> > +     u32 i;
> > +
> > +     /* tx interrupt mitigation */
> > +     for (i = 0; i < tp->func_tx_queue_num; i++)
> > +             rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
> > +
> > +     /* rx interrupt mitigation */
> > +     for (i = 0; i < tp->func_rx_queue_num; i++)
> > +             rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
> > +}
> > +
> > +static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
> > +{
> > +     rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> > +     rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
> > +}
> > +
> >  static void rtase_tally_counter_clear(const struct rtase_private *tp)
> >  {
> >       u32 cmd = lower_32_bits(tp->tally_paddr);
> > @@ -474,6 +493,123 @@ static void rtase_tally_counter_clear(const struct
> rtase_private *tp)
> >       rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
> >  }
> >
> > +static void rtase_desc_addr_fill(const struct rtase_private *tp)
> > +{
> > +     const struct rtase_ring *ring;
> > +     u16 i, cmd, val;
> > +     int err;
> > +
> > +     for (i = 0; i < tp->func_tx_queue_num; i++) {
> > +             ring = &tp->tx_ring[i];
> > +
> > +             rtase_w32(tp, RTASE_TX_DESC_ADDR0,
> > +                       lower_32_bits(ring->phy_addr));
> > +             rtase_w32(tp, RTASE_TX_DESC_ADDR4,
> > +                       upper_32_bits(ring->phy_addr));
> > +
> > +             cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
> > +             rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
> > +
> > +             err = read_poll_timeout(rtase_r16, val, !(val &
> TX_DESC_CMD_CS),
> > +                                     10, 1000, false, tp,
> RTASE_TX_DESC_COMMAND);
> > +
> > +             if (err == -ETIMEDOUT)
> > +                     netdev_err(tp->dev, "error occurred in fill tx
> descriptor\n");
> > +     }
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++) {
> > +             ring = &tp->rx_ring[i];
> > +
> > +             if (i == 0) {
> > +                     rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
> > +                               lower_32_bits(ring->phy_addr));
> > +                     rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
> > +                               upper_32_bits(ring->phy_addr));
> > +             } else {
> > +                     rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i
> - 1) * 8)),
> > +                               lower_32_bits(ring->phy_addr));
> > +                     rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i
> - 1) * 8)),
> > +                               upper_32_bits(ring->phy_addr));
> > +             }
> > +     }
> > +}
> > +
> > +static void rtase_hw_set_features(const struct net_device *dev,
> > +                               netdev_features_t features)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 rx_config, val;
> > +
> > +     rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> > +     if (features & NETIF_F_RXALL)
> > +             rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
> > +     else
> > +             rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
> > +
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> > +
> > +     val = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +     if (features & NETIF_F_RXCSUM)
> > +             rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
> > +     else
> > +             rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
> > +
> > +     rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
> > +     if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
> > +             rx_config |= (INNER_VLAN_DETAG_EN |
> OUTER_VLAN_DETAG_EN);
> > +     else
> > +             rx_config &= ~(INNER_VLAN_DETAG_EN |
> OUTER_VLAN_DETAG_EN);
> > +
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
> > +}
> > +
> > +static void rtase_set_mar(const struct rtase_private *tp)
> > +{
> > +     rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
> > +     rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
> > +}
> > +
> > +static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
> > +     u16 rx_mode;
> > +
> > +     rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
> > +     rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
> > +
> > +     if (dev->flags & IFF_PROMISC) {
> > +             rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
> > +     } else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
> > +                (dev->flags & IFF_ALLMULTI)) {
> > +             /* too many to filter perfectly -- accept all multicasts */
> 
> This comment seems to exist since RTL8139. The filtering seems to be
> hash-based, so there's no perfect filtering anyway.
> What's the benefit of the filter limit?

This restriction doesn't seem to help, I will remove it.
> 
> > +             rx_mode |= ACCEPT_MULTICAST;
> > +     } else {
> > +             struct netdev_hw_addr *hw_addr;
> > +
> > +             mc_filter[0] = 0;
> > +             mc_filter[1] = 0;
> > +
> > +             netdev_for_each_mc_addr(hw_addr, dev) {
> > +                     u32 bit_nr = eth_hw_addr_crc(hw_addr);
> > +                     u32 idx = u32_get_bits(bit_nr, BIT(31));
> > +                     u32 bit = u32_get_bits(bit_nr,
> MULTICAST_FILTER_MASK);
> > +
> > +                     mc_filter[idx] |= BIT(bit);
> > +                     rx_mode |= ACCEPT_MULTICAST;
> > +             }
> > +     }
> > +
> > +     if (dev->features & NETIF_F_RXALL)
> > +             rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
> > +
> > +     tp->mc_filter[0] = swab32(mc_filter[1]);
> > +     tp->mc_filter[1] = swab32(mc_filter[0]);
> > +
> > +     rtase_set_mar(tp);
> 
> Why this function at all? You can set the registers directly here.
> Related question: Why store mc_filter[] in tp?
> It's used nowhere else AFAICS.

This is to prepare for the subsequent vf. We only have one hardware, which needs to be allocated to 8 vfs, so the state must be saved.
> 
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
> > +}
> > +
> >  static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
> >  {
> >       const struct rtase_int_vector *ivec = &tp->int_vector[0];
> > @@ -545,6 +681,113 @@ static void rtase_hw_reset(const struct net_device
> *dev)
> >       rtase_nic_reset(dev);
> >  }
> >
> > +static void rtase_set_rx_queue(const struct rtase_private *tp)
> > +{
> > +     u16 reg_data;
> > +
> > +     reg_data = rtase_r16(tp, RTASE_FCR);
> > +     switch (tp->func_rx_queue_num) {
> > +     case 1:
> > +             u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
> > +             break;
> > +     case 2:
> > +             u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
> > +             break;
> > +     case 4:
> > +             u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
> > +             break;
> > +     }
> > +     rtase_w16(tp, RTASE_FCR, reg_data);
> > +}
> > +
> > +static void rtase_set_tx_queue(const struct rtase_private *tp)
> > +{
> > +     u16 reg_data;
> > +
> > +     reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
> > +     switch (tp->tx_queue_ctrl) {
> > +     case 1:
> > +             u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
> > +             break;
> > +     case 2:
> > +             u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
> > +             break;
> > +     case 3:
> > +     case 4:
> > +             u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
> > +             break;
> > +     default:
> > +             u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
> > +             break;
> > +     }
> > +     rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
> > +}
> > +
> > +static void rtase_hw_config(struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u32 reg_data32;
> > +     u16 reg_data16;
> > +
> > +     rtase_hw_reset(dev);
> > +
> > +     /* Set Rx DMA burst */
> > +     reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
> > +     reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
> > +     u16p_replace_bits(&reg_data16, RX_DMA_BURST_256,
> RX_MX_DMA_MASK);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
> > +
> > +     /* New Rx Descritpor */
> > +     reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
> > +     reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
> > +     u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
> > +
> > +     rtase_set_rx_queue(tp);
> > +
> > +     /* interrupt mitigation */
> > +     rtase_interrupt_mitigation(tp);
> > +
> > +     /* set tx DMA burst size and interframe gap time */
> > +     reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
> > +     u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED,
> TX_DMA_MASK);
> > +     u32p_replace_bits(&reg_data32, INTERFRAMEGAP,
> TX_INTER_FRAME_GAP_MASK);
> > +     rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
> > +
> > +     /* new tx Descriptor */
> > +     reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
> > +     rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 |
> TX_NEW_DESC_FORMAT_EN);
> > +
> > +     /* tx Fetch Desc Number */
> > +     rtase_w8(tp, RTASE_TDFNR, 0x10);
> > +
> > +     /* tag num select */
> > +     reg_data16 = rtase_r16(tp, RTASE_MTPS);
> > +     u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
> > +     rtase_w16(tp, RTASE_MTPS, reg_data16);
> > +
> > +     rtase_set_tx_queue(tp);
> > +
> > +     /* TOK condition */
> > +     rtase_w16(tp, RTASE_TOKSEL, 0x5555);
> > +
> > +     rtase_tally_counter_addr_fill(tp);
> > +     rtase_desc_addr_fill(tp);
> > +     rtase_hw_set_features(dev, dev->features);
> > +
> > +     /* enable flow control */
> > +     reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +     reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> > +     rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
> > +     /* set Near FIFO Threshold - rx missed issue. */
> > +     rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
> > +
> > +     rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
> > +
> > +     /* set Rx packet filter */
> > +     rtase_hw_set_rx_packet_filter(dev);
> > +}
> > +
> >  static void rtase_nic_enable(const struct net_device *dev)
> >  {
> >       const struct rtase_private *tp = netdev_priv(dev);
> > diff --git a/drivers/net/ethernet/realtek/rtase/tt.c
> b/drivers/net/ethernet/realtek/rtase/tt.c
> > new file mode 100644
> > index 000000000000..5ea4d51fcc47
> > --- /dev/null
> > +++ b/drivers/net/ethernet/realtek/rtase/tt.c
> > @@ -0,0 +1,2542 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> > +/*
> > + *  rtase is the Linux device driver released for Realtek Automotive Switch
> > + *  controllers with PCI-Express interface.
> > + *
> > + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> > + *
> > + *  Below is a simplified block diagram of the chip and its relevant
> interfaces.
> > + *
> > + *               *************************
> > + *               *                       *
> > + *               *  CPU network device   *
> > + *               *                       *
> > + *               *   +-------------+     *
> > + *               *   |  PCIE Host  |     *
> > + *               ***********++************
> > + *                          ||
> > + *                         PCIE
> > + *                          ||
> > + *      ********************++**********************
> > + *      *            | PCIE Endpoint |             *
> > + *      *            +---------------+             *
> > + *      *                | GMAC |                  *
> > + *      *                +--++--+  Realtek         *
> > + *      *                   ||     RTL90xx Series  *
> > + *      *                   ||                     *
> > + *      *     +-------------++----------------+    *
> > + *      *     |           | MAC |             |    *
> > + *      *     |           +-----+             |    *
> > + *      *     |                               |    *
> > + *      *     |     Ethernet Switch Core      |    *
> > + *      *     |                               |    *
> > + *      *     |   +-----+           +-----+   |    *
> > + *      *     |   | MAC |...........| MAC |   |    *
> > + *      *     +---+-----+-----------+-----+---+    *
> > + *      *         | PHY |...........| PHY |        *
> > + *      *         +--++-+           +--++-+        *
> > + *      *************||****************||***********
> > + *
> > + *  The block of the Realtek RTL90xx series is our entire chip architecture,
> > + *  the GMAC is connected to the switch core, and there is no PHY in
> between.
> > + *  In addition, this driver is mainly used to control GMAC, but does not
> > + *  control the switch core, so it is not the same as DSA.
> > + */
> > +
> > +#include <linux/crc32.h>
> > +#include <linux/delay.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/if_vlan.h>
> > +#include <linux/in.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/ip.h>
> > +#include <linux/ipv6.h>
> > +#include <linux/mdio.h>
> > +#include <linux/module.h>
> > +#include <linux/moduleparam.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/pci.h>
> > +#include <linux/prefetch.h>
> > +#include <linux/rtnetlink.h>
> > +#include <linux/tcp.h>
> > +#include <asm/irq.h>
> > +#include <net/ip6_checksum.h>
> > +#include <net/page_pool/helpers.h>
> > +#include <net/pkt_cls.h>
> > +
> > +#include "rtase.h"
> > +
> > +#define RTK_OPTS1_DEBUG_VALUE 0x0BADBEEF
> > +#define RTK_MAGIC_NUMBER      0x0BADBADBADBADBAD
> > +
> > +static const struct pci_device_id rtase_pci_tbl[] = {
> > +     {PCI_VDEVICE(REALTEK, 0x906A)},
> > +     {}
> > +};
> > +
> > +MODULE_DEVICE_TABLE(pci, rtase_pci_tbl);
> > +
> > +MODULE_AUTHOR("Realtek ARD Software Team");
> > +MODULE_DESCRIPTION("Network Driver for the PCIe interface of Realtek
> Automotive Ethernet Switch");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > +
> > +struct rtase_counters {
> > +     __le64 tx_packets;
> > +     __le64 rx_packets;
> > +     __le64 tx_errors;
> > +     __le32 rx_errors;
> > +     __le16 rx_missed;
> > +     __le16 align_errors;
> > +     __le32 tx_one_collision;
> > +     __le32 tx_multi_collision;
> > +     __le64 rx_unicast;
> > +     __le64 rx_broadcast;
> > +     __le32 rx_multicast;
> > +     __le16 tx_aborted;
> > +     __le16 tx_underun;
> > +} __packed;
> > +
> 
> You defined this struct in patch 1 already in another source code file.
> Why redefine it?

Sorry this is redundant code, I will remove it in the next version.
> 
> > +static void rtase_w8(const struct rtase_private *tp, u16 reg, u8 val8)
> > +{
> > +     writeb(val8, tp->mmio_addr + reg);
> > +}
> > +
> > +static void rtase_w16(const struct rtase_private *tp, u16 reg, u16 val16)
> > +{
> > +     writew(val16, tp->mmio_addr + reg);
> > +}
> > +
> > +static void rtase_w32(const struct rtase_private *tp, u16 reg, u32 val32)
> > +{
> > +     writel(val32, tp->mmio_addr + reg);
> > +}
> > +
> > +static u8 rtase_r8(const struct rtase_private *tp, u16 reg)
> > +{
> > +     return readb(tp->mmio_addr + reg);
> > +}
> > +
> > +static u16 rtase_r16(const struct rtase_private *tp, u16 reg)
> > +{
> > +     return readw(tp->mmio_addr + reg);
> > +}
> > +
> > +static u32 rtase_r32(const struct rtase_private *tp, u16 reg)
> > +{
> > +     return readl(tp->mmio_addr + reg);
> > +}
> > +
> > +static void rtase_set_rxbufsize(struct rtase_private *tp)
> > +{
> > +     tp->rx_buf_sz = RX_BUF_SIZE;
> > +}
> > +
> > +static int rtase_alloc_desc(struct rtase_private *tp)
> > +{
> > +     struct pci_dev *pdev = tp->pdev;
> > +     u32 i;
> > +
> > +     /* rx and tx descriptors needs 256 bytes alignment.
> > +      * dma_alloc_coherent provides more.
> > +      */
> > +     for (i = 0; i < tp->func_tx_queue_num; i++) {
> > +             tp->tx_ring[i].desc = dma_alloc_coherent(&pdev->dev,
> > +
> RTASE_TX_RING_DESC_SIZE,
> > +
> &tp->tx_ring[i].phy_addr,
> > +
> GFP_KERNEL);
> > +             if (!tp->tx_ring[i].desc)
> > +                     return -ENOMEM;
> > +     }
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++) {
> > +             tp->rx_ring[i].desc =
> > +                     dma_alloc_coherent(&pdev->dev,
> RTASE_RX_RING_DESC_SIZE,
> > +                                        &tp->rx_ring[i].phy_addr,
> > +                                        GFP_KERNEL);
> > +             if (!tp->rx_ring[i].desc)
> > +                     return -ENOMEM;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void rtase_free_desc(struct rtase_private *tp)
> > +{
> > +     struct pci_dev *pdev = tp->pdev;
> > +     u32 i;
> > +
> > +     for (i = 0; i < tp->func_tx_queue_num; i++) {
> > +             if (!tp->tx_ring[i].desc)
> > +                     continue;
> > +
> > +             dma_free_coherent(&pdev->dev,
> RTASE_TX_RING_DESC_SIZE,
> > +                               tp->tx_ring[i].desc,
> > +                               tp->tx_ring[i].phy_addr);
> > +             tp->tx_ring[i].desc = NULL;
> > +     }
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++) {
> > +             if (!tp->rx_ring[i].desc)
> > +                     continue;
> > +
> > +             dma_free_coherent(&pdev->dev,
> RTASE_RX_RING_DESC_SIZE,
> > +                               tp->rx_ring[i].desc,
> > +                               tp->rx_ring[i].phy_addr);
> > +             tp->rx_ring[i].desc = NULL;
> > +     }
> > +}
> > +
> > +static void rtase_unmap_tx_skb(struct pci_dev *pdev, u32 len,
> > +                            struct tx_desc *desc)
> > +{
> > +     dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len,
> > +                      DMA_TO_DEVICE);
> > +     desc->opts1 = cpu_to_le32(RTK_OPTS1_DEBUG_VALUE);
> > +     desc->opts2 = 0x00;
> > +     desc->addr = cpu_to_le64(RTK_MAGIC_NUMBER);
> > +}
> > +
> > +static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n)
> > +{
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     struct net_device *dev = tp->dev;
> > +     struct tx_desc *desc_base = ring->desc;
> > +     u32 i;
> > +
> > +     for (i = 0; i < n; i++) {
> > +             u32 entry = (start + i) % NUM_DESC;
> > +             struct tx_desc *desc = desc_base + entry;
> > +             u32 len = ring->mis.len[entry];
> > +             struct sk_buff *skb;
> > +
> > +             if (len == 0)
> > +                     continue;
> > +
> > +             rtase_unmap_tx_skb(tp->pdev, len, desc);
> > +             ring->mis.len[entry] = 0;
> > +             skb = ring->skbuff[entry];
> > +             if (!skb)
> > +                     continue;
> > +
> > +             dev->stats.tx_dropped++;
> > +             dev_kfree_skb_any(skb);
> > +             ring->skbuff[entry] = NULL;
> > +     }
> > +}
> > +
> > +static void rtase_tx_clear(struct rtase_private *tp)
> > +{
> > +     struct rtase_ring *ring;
> > +     u16 i;
> > +
> > +     for (i = 0; i < tp->func_tx_queue_num; i++) {
> > +             ring = &tp->tx_ring[i];
> > +             rtase_tx_clear_range(ring, ring->dirty_idx, NUM_DESC);
> > +             ring->cur_idx = 0;
> > +             ring->dirty_idx = 0;
> > +     }
> > +}
> > +
> > +static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
> > +{
> > +     u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RING_END;
> > +
> > +     desc->desc_status.opts2 = 0;
> > +     /* force memory writes to complete before releasing descriptor */
> > +     dma_wmb();
> > +     WRITE_ONCE(desc->desc_cmd.opts1,
> > +                cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
> > +}
> > +
> > +static bool rtase_tx_avail(struct rtase_ring *ring)
> > +{
> > +     u32 avail_num = READ_ONCE(ring->dirty_idx) + NUM_DESC -
> > +                     READ_ONCE(ring->cur_idx);
> > +
> > +     return avail_num > MAX_SKB_FRAGS;
> > +}
> > +
> > +static int tx_handler(struct rtase_ring *ring, int budget)
> > +{
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     struct net_device *dev = tp->dev;
> > +     int workdone = 0;
> > +     u32 dirty_tx;
> > +     u32 tx_left;
> > +
> > +     dirty_tx = ring->dirty_idx;
> > +     tx_left = READ_ONCE(ring->cur_idx) - dirty_tx;
> > +
> > +     while (tx_left > 0) {
> > +             u32 entry = dirty_tx % NUM_DESC;
> > +             struct tx_desc *desc = ring->desc +
> > +                                    sizeof(struct tx_desc) * entry;
> > +             u32 len = ring->mis.len[entry];
> > +             u32 status;
> > +
> > +             status = le32_to_cpu(desc->opts1);
> > +
> > +             if (status & DESC_OWN)
> > +                     break;
> > +
> > +             rtase_unmap_tx_skb(tp->pdev, len, desc);
> > +             ring->mis.len[entry] = 0;
> > +             if (ring->skbuff[entry]) {
> > +                     dev_consume_skb_any(ring->skbuff[entry]);
> > +                     ring->skbuff[entry] = NULL;
> > +             }
> > +
> > +             dev->stats.tx_bytes += len;
> > +             dev->stats.tx_packets++;
> > +             dirty_tx++;
> > +             tx_left--;
> > +             workdone++;
> > +
> > +             if (workdone == budget)
> > +                     break;
> > +     }
> > +
> > +     if (ring->dirty_idx != dirty_tx) {
> > +             WRITE_ONCE(ring->dirty_idx, dirty_tx);
> > +
> > +             if (__netif_subqueue_stopped(dev, ring->index) &&
> > +                 rtase_tx_avail(ring))
> > +                     netif_start_subqueue(dev, ring->index);
> > +
> 
> Really no memory barriers needed in this function?

We use READ_ONCE() and WRITE_ONCE() to handle where memory barrier is needed.
> 
> > +             if (ring->cur_idx != dirty_tx)
> > +                     rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
> > +     }
> > +
> > +     return workdone;
> > +}
> > +
> > +static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
> > +{
> > +     struct rtase_ring *ring = &tp->tx_ring[idx];
> > +     struct tx_desc *desc;
> > +     u32 i;
> > +
> > +     memset(ring->desc, 0x0, RTASE_TX_RING_DESC_SIZE);
> > +     memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
> > +     ring->cur_idx = 0;
> > +     ring->dirty_idx = 0;
> > +     ring->index = idx;
> > +
> > +     for (i = 0; i < NUM_DESC; i++) {
> > +             ring->mis.len[i] = 0;
> > +             if ((NUM_DESC - 1) == i) {
> > +                     desc = ring->desc + sizeof(struct tx_desc) * i;
> > +                     desc->opts1 = cpu_to_le32(RING_END);
> > +             }
> > +     }
> > +
> > +     ring->ring_handler = tx_handler;
> > +     if (idx < 4) {
> > +             ring->ivec = &tp->int_vector[idx];
> > +             list_add_tail(&ring->ring_entry,
> > +                           &tp->int_vector[idx].ring_list);
> > +     } else {
> > +             ring->ivec = &tp->int_vector[0];
> > +             list_add_tail(&ring->ring_entry,
> &tp->int_vector[0].ring_list);
> > +     }
> > +}
> > +
> > +static void rtase_map_to_asic(union rx_desc *desc, dma_addr_t mapping,
> > +                           u32 rx_buf_sz)
> > +{
> > +     desc->desc_cmd.addr = cpu_to_le64(mapping);
> > +     /* make sure the physical address has been updated */
> > +     wmb();
> > +     rtase_mark_to_asic(desc, rx_buf_sz);
> > +}
> > +
> > +static void rtase_make_unusable_by_asic(union rx_desc *desc)
> > +{
> > +     desc->desc_cmd.addr = cpu_to_le64(RTK_MAGIC_NUMBER);
> > +     desc->desc_cmd.opts1 &= ~cpu_to_le32(DESC_OWN | RSVD_MASK);
> > +}
> > +
> > +static int rtase_alloc_rx_skb(const struct rtase_ring *ring,
> > +                           struct sk_buff **p_sk_buff, union rx_desc
> *desc,
> > +                           dma_addr_t *rx_phy_addr, u8 in_intr)
> > +{
> > +     struct rtase_int_vector *ivec = ring->ivec;
> > +     const struct rtase_private *tp = ivec->tp;
> > +     struct sk_buff *skb = NULL;
> > +     struct page *page;
> > +     dma_addr_t mapping;
> > +     void *buf_addr;
> > +     int ret = 0;
> > +
> > +     page = page_pool_dev_alloc_pages(tp->page_pool);
> > +     if (!page) {
> > +             netdev_err(tp->dev, "failed to alloc page\n");
> > +             goto err_out;
> > +     }
> > +
> > +     buf_addr = page_address(page);
> > +     mapping = page_pool_get_dma_addr(page);
> > +
> > +     skb = build_skb(buf_addr, PAGE_SIZE);
> > +     if (!skb) {
> > +             page_pool_put_full_page(tp->page_pool, page, true);
> > +             netdev_err(tp->dev, "failed to build skb\n");
> > +             goto err_out;
> > +     }
> > +
> > +     *p_sk_buff = skb;
> > +     *rx_phy_addr = mapping;
> > +     rtase_map_to_asic(desc, mapping, tp->rx_buf_sz);
> > +
> > +     return ret;
> > +
> > +err_out:
> > +     if (skb)
> > +             dev_kfree_skb(skb);
> > +
> > +     ret = -ENOMEM;
> > +     rtase_make_unusable_by_asic(desc);
> > +
> > +     return ret;
> > +}
> > +
> > +static u32 rtase_rx_ring_fill(struct rtase_ring *ring, u32 ring_start,
> > +                           u32 ring_end, u8 in_intr)
> > +{
> > +     union rx_desc *desc_base = ring->desc;
> > +     u32 cur;
> > +
> > +     for (cur = ring_start; ring_end - cur > 0; cur++) {
> > +             u32 i = cur % NUM_DESC;
> > +             union rx_desc *desc = desc_base + i;
> > +             int ret;
> > +
> > +             if (ring->skbuff[i])
> > +                     continue;
> > +
> > +             ret = rtase_alloc_rx_skb(ring, &ring->skbuff[i], desc,
> > +
> &ring->mis.data_phy_addr[i],
> > +                                      in_intr);
> > +             if (ret)
> > +                     break;
> > +     }
> > +
> > +     return cur - ring_start;
> > +}
> > +
> > +static void rtase_mark_as_last_descriptor(union rx_desc *desc)
> > +{
> > +     desc->desc_cmd.opts1 |= cpu_to_le32(RING_END);
> > +}
> > +
> > +static void rtase_rx_ring_clear(struct rtase_ring *ring)
> > +{
> > +     union rx_desc *desc;
> > +     u32 i;
> > +
> > +     for (i = 0; i < NUM_DESC; i++) {
> > +             desc = ring->desc + sizeof(union rx_desc) * i;
> > +
> > +             if (!ring->skbuff[i])
> > +                     continue;
> > +
> > +             dev_kfree_skb(ring->skbuff[i]);
> > +
> > +             ring->skbuff[i] = NULL;
> > +
> > +             rtase_make_unusable_by_asic(desc);
> > +     }
> > +}
> > +
> > +static int rtase_fragmented_frame(u32 status)
> > +{
> > +     return (status & (RX_FIRST_FRAG | RX_LAST_FRAG)) !=
> > +             (RX_FIRST_FRAG | RX_LAST_FRAG);
> > +}
> > +
> > +static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff
> *skb,
> > +                       const union rx_desc *desc)
> > +{
> > +     u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
> > +
> > +     /* rx csum offload */
> > +     if (((opts2 & RX_V4F) && !(opts2 & RX_IPF)) || (opts2 & RX_V6F)) {
> > +             if (((opts2 & RX_TCPT) && !(opts2 & RX_TCPF)) ||
> > +                 ((opts2 & RX_UDPT) && !(opts2 & RX_UDPF))) {
> > +                     skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +             } else {
> > +                     skb->ip_summed = CHECKSUM_NONE;
> > +             }
> > +     } else {
> > +             skb->ip_summed = CHECKSUM_NONE;
> > +     }
> > +}
> > +
> > +static void rtase_rx_vlan_skb(union rx_desc *desc, struct sk_buff *skb)
> > +{
> > +     u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
> > +
> > +     if (!(opts2 & RX_VLAN_TAG))
> > +             return;
> > +
> > +     __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 &
> VLAN_TAG_MASK));
> > +}
> > +
> > +static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb)
> > +{
> > +     struct rtase_int_vector *ivec = ring->ivec;
> > +
> > +     napi_gro_receive(&ivec->napi, skb);
> > +}
> > +
> > +static int rx_handler(struct rtase_ring *ring, int budget)
> > +{
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     u32 pkt_size, cur_rx, delta, entry, status;
> > +     struct net_device *dev = tp->dev;
> > +     union rx_desc *desc_base = ring->desc;
> > +     struct sk_buff *skb;
> > +     union rx_desc *desc;
> > +     int workdone = 0;
> > +
> > +     if (!ring->desc)
> > +             return workdone;
> > +
> > +     cur_rx = ring->cur_idx;
> > +     entry = cur_rx % NUM_DESC;
> > +     desc = &desc_base[entry];
> > +
> > +     do {
> > +             /* make sure discriptor has been updated */
> > +             rmb();
> > +             status = le32_to_cpu(desc->desc_status.opts1);
> > +
> > +             if (status & DESC_OWN)
> > +                     break;
> > +
> > +             if (unlikely(status & RX_RES)) {
> > +                     if (net_ratelimit())
> > +                             netdev_warn(dev, "Rx ERROR. status =
> %08x\n",
> > +                                         status);
> > +
> > +                     dev->stats.rx_errors++;
> > +
> > +                     if (status & (RX_RWT | RX_RUNT))
> > +                             dev->stats.rx_length_errors++;
> > +
> > +                     if (status & RX_CRC)
> > +                             dev->stats.rx_crc_errors++;
> > +
> > +                     if (dev->features & NETIF_F_RXALL)
> > +                             goto process_pkt;
> > +
> > +                     rtase_mark_to_asic(desc, tp->rx_buf_sz);
> > +                     goto skip_process_pkt;
> > +             }
> > +
> > +process_pkt:
> > +             pkt_size = status & RX_PKT_SIZE_MASK;
> > +             if (likely(!(dev->features & NETIF_F_RXFCS)))
> > +                     pkt_size -= ETH_FCS_LEN;
> > +
> > +             /* the driver does not support incoming fragmented
> > +              * frames. they are seen as a symptom of over-mtu
> > +              * sized frames
> > +              */
> > +             if (unlikely(rtase_fragmented_frame(status))) {
> > +                     dev->stats.rx_dropped++;
> > +                     dev->stats.rx_length_errors++;
> > +                     rtase_mark_to_asic(desc, tp->rx_buf_sz);
> > +                     continue;
> > +             }
> > +
> > +             skb = ring->skbuff[entry];
> > +             dma_sync_single_for_cpu(&tp->pdev->dev,
> > +
> ring->mis.data_phy_addr[entry],
> > +                                     tp->rx_buf_sz,
> DMA_FROM_DEVICE);
> > +
> > +             ring->skbuff[entry] = NULL;
> > +
> > +             if (dev->features & NETIF_F_RXCSUM)
> > +                     rtase_rx_csum(tp, skb, desc);
> > +
> > +             skb->dev = dev;
> > +             skb_put(skb, pkt_size);
> > +             skb_mark_for_recycle(skb);
> > +             skb->protocol = eth_type_trans(skb, dev);
> > +
> > +             if (skb->pkt_type == PACKET_MULTICAST)
> > +                     dev->stats.multicast++;
> > +
> > +             rtase_rx_vlan_skb(desc, skb);
> > +             rtase_rx_skb(ring, skb);
> > +
> > +             dev->stats.rx_bytes += pkt_size;
> > +             dev->stats.rx_packets++;
> > +
> > +skip_process_pkt:
> > +             workdone++;
> > +             cur_rx++;
> > +             entry = cur_rx % NUM_DESC;
> > +             desc = ring->desc + sizeof(union rx_desc) * entry;
> > +             prefetch(desc);
> > +     } while (workdone != budget);
> > +
> > +     ring->cur_idx = cur_rx;
> > +     delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx, 1);
> > +
> > +     if (!delta && workdone)
> > +             netdev_info(dev, "no Rx buffer allocated\n");
> > +
> > +     ring->dirty_idx += delta;
> > +
> > +     if ((ring->dirty_idx + NUM_DESC) == ring->cur_idx)
> > +             netdev_emerg(dev, "Rx buffers exhausted\n");
> > +
> > +     return workdone;
> > +}
> > +
> > +static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
> > +{
> > +     struct rtase_ring *ring = &tp->rx_ring[idx];
> > +     u16 i;
> > +
> > +     memset(ring->desc, 0x0, RTASE_RX_RING_DESC_SIZE);
> > +     memset(ring->skbuff, 0x0, sizeof(ring->skbuff));
> > +     ring->cur_idx = 0;
> > +     ring->dirty_idx = 0;
> > +     ring->index = idx;
> > +
> > +     for (i = 0; i < NUM_DESC; i++)
> > +             ring->mis.data_phy_addr[i] = 0;
> > +
> > +     ring->ring_handler = rx_handler;
> > +     ring->ivec = &tp->int_vector[idx];
> > +     list_add_tail(&ring->ring_entry, &tp->int_vector[idx].ring_list);
> > +}
> > +
> > +static void rtase_rx_clear(struct rtase_private *tp)
> > +{
> > +     u32 i;
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++)
> > +             rtase_rx_ring_clear(&tp->rx_ring[i]);
> > +
> > +     page_pool_destroy(tp->page_pool);
> > +     tp->page_pool = NULL;
> > +}
> > +
> > +static int rtase_init_ring(const struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     struct page_pool *page_pool;
> > +     struct page_pool_params pp_params = {
> > +             .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
> > +             .order = 0,
> > +             .pool_size = NUM_DESC * tp->func_rx_queue_num,
> > +             .nid = dev_to_node(&tp->pdev->dev),
> > +             .dev = &tp->pdev->dev,
> > +             .dma_dir = DMA_FROM_DEVICE,
> > +             .max_len = PAGE_SIZE,
> > +             .offset = 0,
> > +     };
> > +     u32 num;
> > +     u16 i;
> > +
> > +     page_pool = page_pool_create(&pp_params);
> > +     if (IS_ERR(page_pool)) {
> > +             netdev_err(tp->dev, "failed to create page pool\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     tp->page_pool = page_pool;
> > +
> > +     for (i = 0; i < tp->func_tx_queue_num; i++)
> > +             rtase_tx_desc_init(tp, i);
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++) {
> > +             rtase_rx_desc_init(tp, i);
> > +             num = rtase_rx_ring_fill(&tp->rx_ring[i], 0, NUM_DESC, 0);
> > +             if (num != NUM_DESC)
> > +                     goto err_out;
> > +
> > +             rtase_mark_as_last_descriptor(tp->rx_ring[i].desc +
> > +                                           sizeof(union rx_desc) *
> > +                                           (NUM_DESC - 1));
> > +     }
> > +
> > +     return 0;
> > +
> > +err_out:
> > +     rtase_rx_clear(tp);
> > +     return -ENOMEM;
> > +}
> > +
> > +static void rtase_interrupt_mitigation(const struct rtase_private *tp)
> > +{
> > +     u32 i;
> > +
> > +     /* tx interrupt mitigation */
> > +     for (i = 0; i < tp->func_tx_queue_num; i++)
> > +             rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit);
> > +
> > +     /* rx interrupt mitigation */
> > +     for (i = 0; i < tp->func_rx_queue_num; i++)
> > +             rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit);
> > +}
> > +
> > +static void rtase_tally_counter_addr_fill(const struct rtase_private *tp)
> > +{
> > +     rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> > +     rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr));
> > +}
> > +
> > +static void rtase_tally_counter_clear(const struct rtase_private *tp)
> > +{
> > +     u32 cmd = lower_32_bits(tp->tally_paddr);
> > +
> > +     rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr));
> > +     rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_RESET);
> > +}
> > +
> > +static void rtase_desc_addr_fill(const struct rtase_private *tp)
> > +{
> > +     const struct rtase_ring *ring;
> > +     u16 i, cmd, val;
> > +     int err;
> > +
> > +     for (i = 0; i < tp->func_tx_queue_num; i++) {
> > +             ring = &tp->tx_ring[i];
> > +
> > +             rtase_w32(tp, RTASE_TX_DESC_ADDR0,
> > +                       lower_32_bits(ring->phy_addr));
> > +             rtase_w32(tp, RTASE_TX_DESC_ADDR4,
> > +                       upper_32_bits(ring->phy_addr));
> > +
> > +             cmd = i | TX_DESC_CMD_WE | TX_DESC_CMD_CS;
> > +             rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd);
> > +
> > +             err = read_poll_timeout(rtase_r16, val, !(val &
> TX_DESC_CMD_CS),
> > +                                     10, 1000, false, tp,
> RTASE_TX_DESC_COMMAND);
> > +
> > +             if (err == -ETIMEDOUT)
> > +                     netdev_err(tp->dev, "error occurred in fill tx
> descriptor\n");
> > +     }
> > +
> > +     for (i = 0; i < tp->func_rx_queue_num; i++) {
> > +             ring = &tp->rx_ring[i];
> > +
> > +             if (i == 0) {
> > +                     rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0,
> > +                               lower_32_bits(ring->phy_addr));
> > +                     rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4,
> > +                               upper_32_bits(ring->phy_addr));
> > +             } else {
> > +                     rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i
> - 1) * 8)),
> > +                               lower_32_bits(ring->phy_addr));
> > +                     rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i
> - 1) * 8)),
> > +                               upper_32_bits(ring->phy_addr));
> > +             }
> > +     }
> > +}
> > +
> > +static void rtase_hw_set_features(const struct net_device *dev,
> > +                               netdev_features_t features)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 rx_config, val;
> > +
> > +     rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> > +     if (features & NETIF_F_RXALL)
> > +             rx_config |= (ACCEPT_ERR | ACCEPT_RUNT);
> > +     else
> > +             rx_config &= ~(ACCEPT_ERR | ACCEPT_RUNT);
> > +
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> > +
> > +     val = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +     if (features & NETIF_F_RXCSUM)
> > +             rtase_w16(tp, RTASE_CPLUS_CMD, val | RX_CHKSUM);
> > +     else
> > +             rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RX_CHKSUM);
> > +
> > +     rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1);
> > +     if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
> > +             rx_config |= (INNER_VLAN_DETAG_EN |
> OUTER_VLAN_DETAG_EN);
> > +     else
> > +             rx_config &= ~(INNER_VLAN_DETAG_EN |
> OUTER_VLAN_DETAG_EN);
> > +
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config);
> > +}
> > +
> > +static void rtase_set_mar(const struct rtase_private *tp)
> > +{
> > +     rtase_w32(tp, RTASE_MAR0, tp->mc_filter[0]);
> > +     rtase_w32(tp, RTASE_MAR1, tp->mc_filter[1]);
> > +}
> > +
> > +static void rtase_hw_set_rx_packet_filter(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
> > +     u16 rx_mode;
> > +
> > +     rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~ACCEPT_MASK;
> > +     rx_mode |= ACCEPT_BROADCAST | ACCEPT_MYPHYS;
> > +
> > +     if (dev->flags & IFF_PROMISC) {
> > +             rx_mode |= ACCEPT_MULTICAST | ACCEPT_ALLPHYS;
> > +     } else if ((netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) ||
> > +                (dev->flags & IFF_ALLMULTI)) {
> > +             /* too many to filter perfectly -- accept all multicasts */
> > +             rx_mode |= ACCEPT_MULTICAST;
> > +     } else {
> > +             struct netdev_hw_addr *hw_addr;
> > +
> > +             mc_filter[0] = 0;
> > +             mc_filter[1] = 0;
> > +
> > +             netdev_for_each_mc_addr(hw_addr, dev) {
> > +                     u32 bit_nr = eth_hw_addr_crc(hw_addr);
> > +                     u32 idx = u32_get_bits(bit_nr, BIT(31));
> > +                     u32 bit = u32_get_bits(bit_nr,
> MULTICAST_FILTER_MASK);
> > +
> > +                     mc_filter[idx] |= BIT(bit);
> > +                     rx_mode |= ACCEPT_MULTICAST;
> > +             }
> > +     }
> > +
> > +     if (dev->features & NETIF_F_RXALL)
> > +             rx_mode |= ACCEPT_ERR | ACCEPT_RUNT;
> > +
> > +     tp->mc_filter[0] = swab32(mc_filter[1]);
> > +     tp->mc_filter[1] = swab32(mc_filter[0]);
> > +
> > +     rtase_set_mar(tp);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode);
> > +}
> > +
> 
> This function is define in rt_main already. You seem to duplicate a lot
> of code. Why?

Sorry, tt.c and tt.h are redundant codes, I will remove them.
> 
> > +static void rtase_irq_dis_and_clear(const struct rtase_private *tp)
> > +{
> > +     const struct rtase_int_vector *ivec = &tp->int_vector[0];
> > +     u32 val1;
> > +     u16 val2;
> > +     u8 i;
> > +
> > +     rtase_w32(tp, ivec->imr_addr, 0);
> > +     val1 = rtase_r32(tp, ivec->isr_addr);
> > +     rtase_w32(tp, ivec->isr_addr, val1);
> > +
> > +     for (i = 1; i < tp->int_nums; i++) {
> > +             ivec = &tp->int_vector[i];
> > +             rtase_w16(tp, ivec->imr_addr, 0);
> > +             val2 = rtase_r16(tp, ivec->isr_addr);
> > +             rtase_w16(tp, ivec->isr_addr, val2);
> > +     }
> > +}
> > +
> > +static void rtase_poll_timeout(const struct rtase_private *tp, u32 cond,
> > +                            u32 sleep_us, u64 timeout_us, u16 reg)
> > +{
> > +     int err;
> > +     u8 val;
> > +
> > +     err = read_poll_timeout(rtase_r8, val, val & cond, sleep_us,
> timeout_us,
> > +                             false, tp, reg);
> > +
> > +     if (err == -ETIMEDOUT)
> > +             netdev_err(tp->dev, "poll reg 0x00%x timeout\n", reg);
> > +}
> > +
> > +static void rtase_nic_reset(const struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 rx_config;
> > +     u8 val;
> > +
> > +     rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config & ~ACCEPT_MASK);
> > +
> > +     val = rtase_r8(tp, RTASE_MISC);
> > +     rtase_w8(tp, RTASE_MISC, val | RX_DV_GATE_EN);
> > +
> > +     val = rtase_r8(tp, RTASE_CHIP_CMD);
> > +     rtase_w8(tp, RTASE_CHIP_CMD, val | STOP_REQ);
> > +     mdelay(2);
> > +
> > +     rtase_poll_timeout(tp, STOP_REQ_DONE, 100, 150000,
> RTASE_CHIP_CMD);
> > +
> > +     rtase_poll_timeout(tp, TX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
> > +
> > +     rtase_poll_timeout(tp, RX_FIFO_EMPTY, 100, 100000, RTASE_FIFOR);
> > +
> > +     val = rtase_r8(tp, RTASE_CHIP_CMD);
> > +     rtase_w8(tp, RTASE_CHIP_CMD, val & ~(TE | RE));
> > +     val = rtase_r8(tp, RTASE_CHIP_CMD);
> > +     rtase_w8(tp, RTASE_CHIP_CMD, val & ~STOP_REQ);
> > +
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config);
> > +}
> > +
> > +static void rtase_hw_reset(const struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     rtase_irq_dis_and_clear(tp);
> > +
> > +     rtase_nic_reset(dev);
> > +}
> > +
> > +static void rtase_set_rx_queue(const struct rtase_private *tp)
> > +{
> > +     u16 reg_data;
> > +
> > +     reg_data = rtase_r16(tp, RTASE_FCR);
> > +     switch (tp->func_rx_queue_num) {
> > +     case 1:
> > +             u16p_replace_bits(&reg_data, 0x1, FCR_RXQ_MASK);
> > +             break;
> > +     case 2:
> > +             u16p_replace_bits(&reg_data, 0x2, FCR_RXQ_MASK);
> > +             break;
> > +     case 4:
> > +             u16p_replace_bits(&reg_data, 0x3, FCR_RXQ_MASK);
> > +             break;
> > +     }
> > +     rtase_w16(tp, RTASE_FCR, reg_data);
> > +}
> > +
> > +static void rtase_set_tx_queue(const struct rtase_private *tp)
> > +{
> > +     u16 reg_data;
> > +
> > +     reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1);
> > +     switch (tp->tx_queue_ctrl) {
> > +     case 1:
> > +             u16p_replace_bits(&reg_data, 0x0, TC_MODE_MASK);
> > +             break;
> > +     case 2:
> > +             u16p_replace_bits(&reg_data, 0x1, TC_MODE_MASK);
> > +             break;
> > +     case 3:
> > +     case 4:
> > +             u16p_replace_bits(&reg_data, 0x2, TC_MODE_MASK);
> > +             break;
> > +     default:
> > +             u16p_replace_bits(&reg_data, 0x3, TC_MODE_MASK);
> > +             break;
> > +     }
> > +     rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data);
> > +}
> > +
> > +static void rtase_hw_config(struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u32 reg_data32;
> > +     u16 reg_data16;
> > +
> > +     rtase_hw_reset(dev);
> > +
> > +     /* Set Rx DMA burst */
> > +     reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0);
> > +     reg_data16 &= ~(RX_SINGLE_TAG | RX_SINGLE_FETCH);
> > +     u16p_replace_bits(&reg_data16, RX_DMA_BURST_256,
> RX_MX_DMA_MASK);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16);
> > +
> > +     /* New Rx Descritpor */
> > +     reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1);
> > +     reg_data16 |= RX_NEW_DESC_FORMAT_EN | PCIE_NEW_FLOW;
> > +     u16p_replace_bits(&reg_data16, 0xF, RX_MAX_FETCH_DESC_MASK);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16);
> > +
> > +     rtase_set_rx_queue(tp);
> > +
> > +     /* interrupt mitigation */
> > +     rtase_interrupt_mitigation(tp);
> > +
> > +     /* set tx DMA burst size and interframe gap time */
> > +     reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0);
> > +     u32p_replace_bits(&reg_data32, TX_DMA_BURST_UNLIMITED,
> TX_DMA_MASK);
> > +     u32p_replace_bits(&reg_data32, INTERFRAMEGAP,
> TX_INTER_FRAME_GAP_MASK);
> > +     rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32);
> > +
> > +     /* new tx Descriptor */
> > +     reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL);
> > +     rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 |
> TX_NEW_DESC_FORMAT_EN);
> > +
> > +     /* tx Fetch Desc Number */
> > +     rtase_w8(tp, RTASE_TDFNR, 0x10);
> > +
> > +     /* tag num select */
> > +     reg_data16 = rtase_r16(tp, RTASE_MTPS);
> > +     u16p_replace_bits(&reg_data16, 0x4, TAG_NUM_SEL_MASK);
> > +     rtase_w16(tp, RTASE_MTPS, reg_data16);
> > +
> > +     rtase_set_tx_queue(tp);
> > +
> > +     /* TOK condition */
> > +     rtase_w16(tp, RTASE_TOKSEL, 0x5555);
> > +
> > +     rtase_tally_counter_addr_fill(tp);
> > +     rtase_desc_addr_fill(tp);
> > +     rtase_hw_set_features(dev, dev->features);
> > +
> > +     /* enable flow control */
> > +     reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +     reg_data16 |= (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> > +     rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16);
> > +     /* set Near FIFO Threshold - rx missed issue. */
> > +     rtase_w16(tp, RTASE_RFIFONFULL, 0x190);
> > +
> > +     rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz);
> > +
> > +     /* set Rx packet filter */
> > +     rtase_hw_set_rx_packet_filter(dev);
> > +}
> > +
> > +static void rtase_nic_enable(const struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 rcr = rtase_r16(tp, RTASE_RX_CONFIG_1);
> > +     u8 val;
> > +
> > +     /* PCIe PLA reload */
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, rcr & ~PCIE_RELOAD_En);
> > +     rtase_w16(tp, RTASE_RX_CONFIG_1, rcr | PCIE_RELOAD_En);
> > +
> > +     /* set PCIe TE & RE */
> > +     val = rtase_r8(tp, RTASE_CHIP_CMD);
> > +     rtase_w8(tp, RTASE_CHIP_CMD, val | TE | RE);
> > +
> > +     /* clear rxdv_gated_en */
> > +     val = rtase_r8(tp, RTASE_MISC);
> > +     rtase_w8(tp, RTASE_MISC, val & ~RX_DV_GATE_EN);
> > +}
> > +
> > +static void rtase_enable_hw_interrupt(const struct rtase_private *tp)
> > +{
> > +     const struct rtase_int_vector *ivec = &tp->int_vector[0];
> > +     u32 i;
> > +
> > +     rtase_w32(tp, ivec->imr_addr, ivec->imr);
> > +
> > +     for (i = 1; i < tp->int_nums; i++) {
> > +             ivec = &tp->int_vector[i];
> > +             rtase_w16(tp, ivec->imr_addr, ivec->imr);
> > +     }
> > +}
> > +
> > +static void rtase_hw_start(const struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     rtase_nic_enable(dev);
> > +     rtase_enable_hw_interrupt(tp);
> > +}
> > +
> > +/*  the interrupt handler does RXQ0 and TXQ0, TXQ4~7 interrutp status
> > + */
> > +static irqreturn_t rtase_interrupt(int irq, void *dev_instance)
> > +{
> > +     const struct rtase_private *tp;
> > +     struct rtase_int_vector *ivec;
> > +     u32 status;
> > +
> > +     ivec = dev_instance;
> > +     tp = ivec->tp;
> > +     status = rtase_r32(tp, ivec->isr_addr);
> > +
> > +     rtase_w32(tp, ivec->imr_addr, 0x0);
> > +     rtase_w32(tp, ivec->isr_addr, status & ~FOVW);
> > +
> > +     if (napi_schedule_prep(&ivec->napi))
> > +             __napi_schedule(&ivec->napi);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +/*  the interrupt handler does RXQ1&TXQ1 or RXQ2&TXQ2 or
> RXQ3&TXQ3 interrupt
> > + *  status according to interrupt vector
> > + */
> > +static irqreturn_t rtase_q_interrupt(int irq, void *dev_instance)
> > +{
> > +     const struct rtase_private *tp;
> > +     struct rtase_int_vector *ivec;
> > +     u16 status;
> > +
> > +     ivec = dev_instance;
> > +     tp = ivec->tp;
> > +     status = rtase_r16(tp, ivec->isr_addr);
> > +
> > +     rtase_w16(tp, ivec->imr_addr, 0x0);
> > +     rtase_w16(tp, ivec->isr_addr, status);
> > +
> > +     if (napi_schedule_prep(&ivec->napi))
> > +             __napi_schedule(&ivec->napi);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int rtase_poll(struct napi_struct *napi, int budget)
> > +{
> > +     const struct rtase_int_vector *ivec;
> > +     const struct rtase_private *tp;
> > +     struct rtase_ring *ring;
> > +     int total_workdone = 0;
> > +
> > +     ivec = container_of(napi, struct rtase_int_vector, napi);
> > +     tp = ivec->tp;
> > +
> > +     list_for_each_entry(ring, &ivec->ring_list, ring_entry) {
> > +             total_workdone += ring->ring_handler(ring, budget);
> > +     }
> > +
> > +     if (total_workdone >= budget)
> > +             return budget;
> > +
> > +     if (napi_complete_done(napi, total_workdone)) {
> > +             if (!ivec->index)
> > +                     rtase_w32(tp, ivec->imr_addr, ivec->imr);
> > +             else
> > +                     rtase_w16(tp, ivec->imr_addr, ivec->imr);
> > +     }
> > +
> > +     return total_workdone;
> > +}
> > +
> > +static int rtase_open(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     struct rtase_int_vector *ivec = &tp->int_vector[0];
> > +     const struct pci_dev *pdev = tp->pdev;
> > +     int ret;
> > +     u16 i;
> > +
> > +     rtase_set_rxbufsize(tp);
> > +
> > +     ret = rtase_alloc_desc(tp);
> > +     if (ret)
> > +             goto err_free_all_allocated_mem;
> > +
> > +     ret = rtase_init_ring(dev);
> > +     if (ret)
> > +             goto err_free_all_allocated_mem;
> > +
> > +     INIT_DELAYED_WORK(&tp->task, NULL);
> > +
> 
> Why not just init once in probe()?

This is redundant code, I will remove it directly.
> 
> > +     rtase_hw_config(dev);
> > +
> > +     if (tp->sw_flag & SWF_MSIX_ENABLED) {
> > +             ret = request_irq(ivec->irq, rtase_interrupt, 0,
> > +                               dev->name, ivec);
> > +
> > +             /* request other interrupts to handle multiqueue */
> > +             for (i = 1; i < tp->int_nums; i++) {
> > +                     if (ret)
> > +                             continue;
> > +
> > +                     ivec = &tp->int_vector[i];
> > +                     if (ivec->status != 1)
> > +                             continue;
> > +
> > +                     snprintf(ivec->name, sizeof(ivec->name),
> "%s_int%i", tp->dev->name, i);
> > +                     ret = request_irq(ivec->irq, rtase_q_interrupt, 0,
> > +                                       ivec->name, ivec);
> > +             }
> > +     } else if (tp->sw_flag & SWF_MSI_ENABLED) {
> > +             ret = request_irq(pdev->irq, rtase_interrupt, 0, dev->name,
> > +                               ivec);
> > +     } else {
> > +             ret = request_irq(pdev->irq, rtase_interrupt, IRQF_SHARED,
> > +                               dev->name, ivec);
> > +     }
> > +
> > +     if (ret != 0) {
> > +             netdev_err(dev, "can't request MSIX interrupt. Error: %d\n",
> ret);
> > +             goto err_free_all_allocated_mem;
> > +     }
> > +
> > +     rtase_hw_start(dev);
> > +
> > +     netif_carrier_on(dev);
> > +     netif_wake_queue(dev);
> > +
> > +     goto out;
> > +
> > +err_free_all_allocated_mem:
> > +     rtase_free_desc(tp);
> > +
> > +out:
> > +     return ret;
> > +}
> > +
> > +static void rtase_down(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u32 i;
> > +
> > +     netif_stop_queue(dev);
> > +
> > +     /* give a racing hard_start_xmit a few cycles to complete */
> > +     synchronize_rcu();
> > +
> > +     netif_carrier_off(dev);
> > +
> > +     rtase_hw_reset(dev);
> > +
> > +     for (i = 0; i < tp->int_nums; i++)
> > +             synchronize_irq(tp->int_vector[i].irq);
> > +
> > +     rtase_tx_clear(tp);
> > +
> > +     rtase_rx_clear(tp);
> > +}
> > +
> > +static int rtase_close(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     const struct pci_dev *pdev = tp->pdev;
> > +     u32 i;
> > +
> > +     rtase_down(dev);
> > +
> > +     if (tp->sw_flag & SWF_MSIX_ENABLED) {
> > +             for (i = 0; i < tp->int_nums; i++)
> > +                     free_irq(tp->int_vector[i].irq, &tp->int_vector[i]);
> > +
> > +     } else {
> > +             free_irq(pdev->irq, &tp->int_vector[0]);
> > +     }
> > +
> > +     rtase_free_desc(tp);
> > +
> > +     return 0;
> > +}
> > +
> > +static u32 rtase_tx_vlan_tag(const struct rtase_private *tp,
> > +                          const struct sk_buff *skb)
> > +{
> > +     return (skb_vlan_tag_present(skb)) ?
> > +             (TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00;
> > +}
> > +
> > +static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
> > +{
> > +     u8 ip_protocol;
> > +     u32 csum_cmd;
> > +
> > +     switch (vlan_get_protocol(skb)) {
> > +     case htons(ETH_P_IP):
> > +             csum_cmd = TX_IPCS_C;
> > +             ip_protocol = ip_hdr(skb)->protocol;
> > +             break;
> > +
> > +     case htons(ETH_P_IPV6):
> > +             csum_cmd = TX_IPV6F_C;
> > +             ip_protocol = ipv6_hdr(skb)->nexthdr;
> > +             break;
> > +
> > +     default:
> > +             ip_protocol = IPPROTO_RAW;
> > +             break;
> > +     }
> > +
> > +     if (ip_protocol == IPPROTO_TCP)
> > +             csum_cmd |= TX_TCPCS_C;
> > +     else if (ip_protocol == IPPROTO_UDP)
> > +             csum_cmd |= TX_UDPCS_C;
> > +     else
> > +             WARN_ON_ONCE(1);
> > +
> > +     csum_cmd |= u32_encode_bits(skb_transport_offset(skb),
> TCPHO_MASK);
> > +
> > +     return csum_cmd;
> > +}
> > +
> > +static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
> > +                         u32 opts1, u32 opts2)
> > +{
> > +     const struct skb_shared_info *info = skb_shinfo(skb);
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     const u8 nr_frags = info->nr_frags;
> > +     struct tx_desc *txd = NULL;
> > +     u32 cur_frag, entry;
> > +     u64 pkt_len_cnt = 0;
> > +
> > +     entry = ring->cur_idx;
> > +     for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
> > +             const skb_frag_t *frag = &info->frags[cur_frag];
> > +             dma_addr_t mapping;
> > +             u32 status, len;
> > +             void *addr;
> > +
> > +             entry = (entry + 1) % NUM_DESC;
> > +
> > +             txd = ring->desc + sizeof(struct tx_desc) * entry;
> > +             len = skb_frag_size(frag);
> > +             addr = skb_frag_address(frag);
> > +             mapping = dma_map_single(&tp->pdev->dev, addr, len,
> > +                                      DMA_TO_DEVICE);
> > +
> > +             if (unlikely(dma_mapping_error(&tp->pdev->dev,
> mapping))) {
> > +                     if (unlikely(net_ratelimit()))
> > +                             netdev_err(tp->dev,
> > +                                        "Failed to map TX
> fragments DMA!\n");
> > +
> > +                     goto err_out;
> > +             }
> > +
> > +             if (((entry + 1) % NUM_DESC) == 0)
> > +                     status = (opts1 | len | RING_END);
> > +             else
> > +                     status = opts1 | len;
> > +
> > +             if (cur_frag == (nr_frags - 1)) {
> > +                     ring->skbuff[entry] = skb;
> > +                     status |= TX_LAST_FRAG;
> > +             }
> > +
> > +             ring->mis.len[entry] = len;
> > +             txd->addr = cpu_to_le64(mapping);
> > +             txd->opts2 = cpu_to_le32(opts2);
> > +
> > +             /* make sure the operating fields have been updated */
> > +             wmb();
> > +             txd->opts1 = cpu_to_le32(status);
> > +             pkt_len_cnt += len;
> > +     }
> > +
> > +     return cur_frag;
> > +
> > +err_out:
> > +     rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
> > +     return -EIO;
> > +}
> > +
> > +static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
> > +                                 struct net_device *dev)
> > +{
> > +     struct skb_shared_info *shinfo = skb_shinfo(skb);
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u32 q_idx, entry, len, opts1, opts2;
> > +     u32 mss = shinfo->gso_size;
> > +     struct rtase_ring *ring;
> > +     struct tx_desc *txd;
> > +     dma_addr_t mapping;
> > +     bool stop_queue;
> > +     int frags;
> > +
> > +     /* multiqueues */
> > +     q_idx = skb_get_queue_mapping(skb);
> > +     ring = &tp->tx_ring[q_idx];
> > +
> > +     if (unlikely(!rtase_tx_avail(ring))) {
> > +             if (net_ratelimit())
> > +                     netdev_err(dev, "BUG! Tx Ring full when queue
> awake!\n");
> > +             goto err_stop;
> > +     }
> > +
> > +     entry = ring->cur_idx % NUM_DESC;
> > +     txd = ring->desc + sizeof(struct tx_desc) * entry;
> > +
> > +     opts1 = DESC_OWN;
> > +     opts2 = rtase_tx_vlan_tag(tp, skb);
> > +
> > +     /* tcp segmentation offload (or tcp large send) */
> > +     if (mss) {
> > +             if (shinfo->gso_type & SKB_GSO_TCPV4) {
> > +                     opts1 |= GIANT_SEND_V4;
> > +             } else if (shinfo->gso_type & SKB_GSO_TCPV6) {
> > +                     if (skb_cow_head(skb, 0))
> > +                             goto err_dma_0;
> > +
> > +                     tcp_v6_gso_csum_prep(skb);
> > +                     opts1 |= GIANT_SEND_V6;
> > +             } else {
> > +                     WARN_ON_ONCE(1);
> > +             }
> > +
> > +             opts1 |= u32_encode_bits(skb_transport_offset(skb),
> TCPHO_MASK);
> > +             opts2 |= u32_encode_bits(mss, MSS_MASK);
> > +     } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
> > +             opts2 |= rtase_tx_csum(skb, dev);
> > +     }
> > +
> > +     frags = rtase_xmit_frags(ring, skb, opts1, opts2);
> > +     if (unlikely(frags < 0))
> > +             goto err_dma_0;
> > +
> > +     if (frags) {
> > +             len = skb_headlen(skb);
> > +             opts1 |= TX_FIRST_FRAG;
> > +     } else {
> > +             len = skb->len;
> > +             ring->skbuff[entry] = skb;
> > +             opts1 |= TX_FIRST_FRAG | TX_LAST_FRAG;
> > +     }
> > +
> > +     if (((entry + 1) % NUM_DESC) == 0)
> > +             opts1 |= (len | RING_END);
> > +     else
> > +             opts1 |= len;
> > +
> > +     mapping = dma_map_single(&tp->pdev->dev, skb->data, len,
> > +                              DMA_TO_DEVICE);
> > +
> > +     if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
> > +             if (unlikely(net_ratelimit()))
> > +                     netdev_err(dev, "Failed to map TX DMA!\n");
> > +
> > +             goto err_dma_1;
> > +     }
> > +
> > +     ring->mis.len[entry] = len;
> > +     txd->addr = cpu_to_le64(mapping);
> > +     txd->opts2 = cpu_to_le32(opts2);
> > +     txd->opts1 = cpu_to_le32(opts1 & ~DESC_OWN);
> > +
> > +     /* make sure the operating fields have been updated */
> > +     wmb();
> > +
> > +     txd->opts1 = cpu_to_le32(opts1);
> > +
> > +     skb_tx_timestamp(skb);
> > +
> > +     /* tx needs to see descriptor changes before updated cur_idx */
> > +     smp_wmb();
> > +
> > +     WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1);
> > +
> > +     stop_queue = !rtase_tx_avail(ring);
> > +     if (unlikely(stop_queue))
> > +             netif_stop_subqueue(dev, q_idx);
> > +
> > +     /* set polling bit */
> > +     rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
> > +
> > +     if (unlikely(stop_queue)) {
> > +             /* make sure cur_idx and dirty_idx have been updated */
> > +             smp_rmb();
> > +             if (rtase_tx_avail(ring))
> > +                     netif_start_subqueue(dev, q_idx);
> > +     }
> > +
> > +     return NETDEV_TX_OK;
> > +
> > +err_dma_1:
> > +     ring->skbuff[entry] = NULL;
> > +     rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
> > +
> > +err_dma_0:
> > +     dev->stats.tx_dropped++;
> > +     dev_kfree_skb_any(skb);
> > +     return NETDEV_TX_OK;
> > +
> > +err_stop:
> > +     netif_stop_queue(dev);
> > +     dev->stats.tx_dropped++;
> > +     return NETDEV_TX_BUSY;
> > +}
> > +
> > +static void rtase_set_rx_mode(struct net_device *dev)
> > +{
> > +     rtase_hw_set_rx_packet_filter(dev);
> > +}
> > +
> > +static void rtase_enable_eem_write(const struct rtase_private *tp)
> > +{
> > +     u8 val;
> > +
> > +     val = rtase_r8(tp, RTASE_EEM);
> > +     rtase_w8(tp, RTASE_EEM, val | EEM_UNLOCK);
> > +}
> > +
> > +static void rtase_disable_eem_write(const struct rtase_private *tp)
> > +{
> > +     u8 val;
> > +
> > +     val = rtase_r8(tp, RTASE_EEM);
> > +     rtase_w8(tp, RTASE_EEM, val & ~EEM_UNLOCK);
> > +}
> > +
> > +static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
> > +{
> > +     u32 rar_low, rar_high;
> > +
> > +     rar_low = (u32)addr[0] | ((u32)addr[1] << 8) |
> > +               ((u32)addr[2] << 16) | ((u32)addr[3] << 24);
> > +
> > +     rar_high = (u32)addr[4] | ((u32)addr[5] << 8);
> > +
> > +     rtase_enable_eem_write(tp);
> > +     rtase_w32(tp, RTASE_MAC0, rar_low);
> > +     rtase_w32(tp, RTASE_MAC4, rar_high);
> > +     rtase_disable_eem_write(tp);
> > +     rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
> > +}
> > +
> > +static int rtase_set_mac_address(struct net_device *dev, void *p)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     int ret;
> > +
> > +     ret = eth_mac_addr(dev, p);
> > +     if (ret)
> > +             return ret;
> > +
> > +     rtase_rar_set(tp, dev->dev_addr);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rtase_change_mtu(struct net_device *dev, int new_mtu)
> > +{
> > +     dev->mtu = new_mtu;
> > +
> > +     netdev_update_features(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static void rtase_wait_for_quiescence(const struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     struct rtase_int_vector *ivec;
> > +     u32 i;
> > +
> > +     for (i = 0; i < tp->int_nums; i++) {
> > +             ivec = &tp->int_vector[i];
> > +             synchronize_irq(ivec->irq);
> > +             /* wait for any pending NAPI task to complete */
> > +             napi_disable(&ivec->napi);
> > +     }
> > +
> > +     rtase_irq_dis_and_clear(tp);
> > +
> > +     for (i = 0; i < tp->int_nums; i++) {
> > +             ivec = &tp->int_vector[i];
> > +             napi_enable(&ivec->napi);
> > +     }
> > +}
> > +
> > +static void rtase_sw_reset(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     int ret;
> > +
> > +     netif_stop_queue(dev);
> > +     netif_carrier_off(dev);
> > +     rtase_hw_reset(dev);
> > +
> > +     /* let's wait a bit while any (async) irq lands on */
> > +     rtase_wait_for_quiescence(dev);
> > +     rtase_tx_clear(tp);
> > +     rtase_rx_clear(tp);
> > +
> > +     ret = rtase_init_ring(dev);
> > +     if (ret) {
> > +             netdev_err(dev, "unable to init ring\n");
> > +             rtase_free_desc(tp);
> > +             return;
> > +     }
> > +
> > +     rtase_hw_config(dev);
> > +     /* always link, so start to transmit & receive */
> > +     rtase_hw_start(dev);
> > +
> > +     netif_carrier_on(dev);
> > +     netif_wake_queue(dev);
> > +}
> > +
> > +static void rtase_dump_tally_counter(const struct rtase_private *tp)
> > +{
> > +     dma_addr_t paddr = tp->tally_paddr;
> > +     u32 cmd = lower_32_bits(paddr);
> > +     u32 val;
> > +     int err;
> > +
> > +     rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(paddr));
> > +     rtase_w32(tp, RTASE_DTCCR0, cmd);
> > +     rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_DUMP);
> > +
> > +     err = read_poll_timeout(rtase_r32, val, !(val & COUNTER_DUMP), 10,
> 250,
> > +                             false, tp, RTASE_DTCCR0);
> > +
> > +     if (err == -ETIMEDOUT)
> > +             netdev_err(tp->dev, "error occurred in dump tally
> counter\n");
> > +}
> > +
> > +static void rtase_dump_state(const struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters;
> > +     int max_reg_size = RTASE_PCI_REGS_SIZE;
> > +     const struct rtase_ring *ring;
> > +     u32 dword_rd;
> > +     int n = 0;
> > +
> > +     ring = &tp->tx_ring[0];
> > +     netdev_err(dev, "Tx descriptor info:\n");
> > +     netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
> > +     netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
> > +     netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
> > +
> > +     ring = &tp->rx_ring[0];
> > +     netdev_err(dev, "Rx descriptor info:\n");
> > +     netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
> > +     netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
> > +     netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
> > +
> > +     netdev_err(dev, "Device Registers:\n");
> > +     netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp,
> RTASE_CHIP_CMD));
> > +     netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
> > +     netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
> > +     netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
> > +                rtase_r16(tp, RTASE_BOOT_CTL));
> > +     netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
> > +                rtase_r16(tp, RTASE_EPHY_ISR));
> > +     netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
> > +                rtase_r16(tp, RTASE_EPHY_IMR));
> > +     netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
> > +                rtase_r16(tp, RTASE_CLKSW_SET));
> > +
> > +     netdev_err(dev, "Dump PCI Registers:\n");
> > +
> > +     while (n < max_reg_size) {
> > +             if ((n % RTASE_DWORD_MOD) == 0)
> > +                     netdev_err(tp->dev, "0x%03x:\n", n);
> > +
> > +             pci_read_config_dword(tp->pdev, n, &dword_rd);
> > +             netdev_err(tp->dev, "%08x\n", dword_rd);
> > +             n += 4;
> > +     }
> > +
> > +     netdev_err(dev, "Dump tally counter:\n");
> > +     counters = tp->tally_vaddr;
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     netdev_err(dev, "tx_packets %lld\n",
> > +                le64_to_cpu(counters->tx_packets));
> > +     netdev_err(dev, "rx_packets %lld\n",
> > +                le64_to_cpu(counters->rx_packets));
> > +     netdev_err(dev, "tx_errors %lld\n",
> > +                le64_to_cpu(counters->tx_errors));
> > +     netdev_err(dev, "rx_missed %lld\n",
> > +                le64_to_cpu(counters->rx_missed));
> > +     netdev_err(dev, "align_errors %lld\n",
> > +                le64_to_cpu(counters->align_errors));
> > +     netdev_err(dev, "tx_one_collision %lld\n",
> > +                le64_to_cpu(counters->tx_one_collision));
> > +     netdev_err(dev, "tx_multi_collision %lld\n",
> > +                le64_to_cpu(counters->tx_multi_collision));
> > +     netdev_err(dev, "rx_unicast %lld\n",
> > +                le64_to_cpu(counters->rx_unicast));
> > +     netdev_err(dev, "rx_broadcast %lld\n",
> > +                le64_to_cpu(counters->rx_broadcast));
> > +     netdev_err(dev, "rx_multicast %lld\n",
> > +                le64_to_cpu(counters->rx_multicast));
> > +     netdev_err(dev, "tx_aborted %lld\n",
> > +                le64_to_cpu(counters->tx_aborted));
> > +     netdev_err(dev, "tx_underun %lld\n",
> > +                le64_to_cpu(counters->tx_underun));
> > +}
> > +
> > +static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue)
> > +{
> > +     rtase_dump_state(dev);
> > +     rtase_sw_reset(dev);
> > +}
> > +
> > +static void rtase_get_stats64(struct net_device *dev,
> > +                           struct rtnl_link_stats64 *stats)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters = tp->tally_vaddr;
> > +
> > +     if (!counters)
> > +             return;
> > +
> > +     netdev_stats_to_stats64(stats, &dev->stats);
> > +     dev_fetch_sw_netstats(stats, dev->tstats);
> > +
> > +     /* fetch additional counter values missing in stats collected by driver
> > +      * from tally counter
> > +      */
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     stats->tx_errors = le64_to_cpu(counters->tx_errors);
> > +     stats->collisions = le32_to_cpu(counters->tx_multi_collision);
> > +     stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted);
> > +     stats->rx_missed_errors = le16_to_cpu(counters->rx_missed);
> > +}
> > +
> > +static void rtase_enable_vlan_filter(const struct rtase_private *tp, bool
> enabled)
> > +{
> > +     u16 tmp;
> > +
> > +     if (enabled == 1) {
> > +             tmp = rtase_r16(tp, RTASE_FCR);
> > +             if (!(tmp & FCR_VLAN_FTR_EN))
> > +                     rtase_w16(tp, RTASE_FCR, tmp |
> FCR_VLAN_FTR_EN);
> > +
> > +             tmp = rtase_r16(tp, RTASE_PCPR);
> > +             if (!(tmp & PCPR_VLAN_FTR_EN))
> > +                     rtase_w16(tp, RTASE_PCPR, tmp |
> PCPR_VLAN_FTR_EN);
> > +     } else {
> > +             tmp = rtase_r16(tp, RTASE_FCR);
> > +             if (tmp & FCR_VLAN_FTR_EN)
> > +                     rtase_w16(tp, RTASE_FCR, tmp &
> ~FCR_VLAN_FTR_EN);
> > +
> > +             tmp = rtase_r16(tp, RTASE_PCPR);
> > +             if (!(tmp & PCPR_VLAN_FTR_EN))
> > +                     rtase_w16(tp, RTASE_PCPR, tmp &
> ~PCPR_VLAN_FTR_EN);
> > +     }
> > +}
> > +
> > +static int rtase_vlan_rx_add_vid(struct net_device *dev, __be16 protocol,
> > +                              u16 vid)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u16 tmp_mem, i;
> > +
> > +     if (be16_to_cpu(protocol) != ETH_P_8021Q)
> > +             return -EINVAL;
> > +
> > +     for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
> > +             u16 addr, mask;
> > +
> > +             if (!(tp->vlan_filter_ctrl & BIT(i))) {
> > +                     tp->vlan_filter_ctrl |= BIT(i);
> > +                     tp->vlan_filter_vid[i] = vid;
> > +                     rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4,
> > +                               vid | VLAN_ENTRY_CAREBIT);
> > +                     /* each 16-bit register contains two VLAN entries
> */
> > +                     addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
> > +                     mask = 0x1 << ((i & 0x1) * 8);
> > +                     tmp_mem = rtase_r16(tp, addr);
> > +                     tmp_mem |= mask;
> > +                     rtase_w16(tp, addr, tmp_mem);
> > +                     break;
> > +             }
> > +     }
> > +
> > +     if (i == RTASE_VLAN_FILTER_ENTRY_NUM)
> > +             return -ENOENT;
> > +
> > +     rtase_enable_vlan_filter(tp, true);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rtase_vlan_rx_kill_vid(struct net_device *dev, __be16 protocol,
> > +                               u16 vid)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u16 tmp_mem, i;
> > +
> > +     if (be16_to_cpu(protocol) != ETH_P_8021Q)
> > +             return -EINVAL;
> > +
> > +     for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
> > +             u16 addr, mask;
> > +
> > +             if (tp->vlan_filter_vid[i] == vid) {
> > +                     tp->vlan_filter_ctrl &= ~BIT(i);
> > +                     tp->vlan_filter_vid[i] = 0;
> > +                     rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
> > +
> > +                     /* each 16-bit register contains two VLAN entries
> */
> > +                     addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
> > +                     mask = ~(0x1 << ((i & 0x1) * 8));
> > +                     tmp_mem = rtase_r16(tp, addr);
> > +                     tmp_mem &= mask;
> > +                     rtase_w16(tp, addr, tmp_mem);
> > +                     break;
> > +             }
> > +     }
> > +
> > +     /* check vlan filter enabled */
> > +     if (!tp->vlan_filter_ctrl)
> > +             rtase_enable_vlan_filter(tp, false);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_NET_POLL_CONTROLLER
> > +/* Polling 'interrupt' - used by things like netconsole to send skbs
> > + * without having to re-enable interrupts. It's not called while
> > + * the interrupt routine is executing.
> > + */
> > +static void rtase_netpoll(struct net_device *dev)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     const struct pci_dev *pdev = tp->pdev;
> > +
> > +     disable_irq(pdev->irq);
> > +     rtase_interrupt(pdev->irq, dev);
> > +     enable_irq(pdev->irq);
> > +}
> > +#endif
> > +
> > +static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue)
> > +{
> > +     u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK;
> > +     u32 val, i;
> > +
> > +     val = u32_encode_bits(idle / RTASE_1T_POWER,
> RTASE_IDLESLOPE_INT_MASK);
> > +     idle %= RTASE_1T_POWER;
> > +
> > +     for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) {
> > +             idle *= 2;
> > +             if ((idle / RTASE_1T_POWER) == 1)
> > +                     val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i);
> > +
> > +             idle %= RTASE_1T_POWER;
> > +     }
> > +     rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val);
> > +}
> > +
> > +static void rtase_setup_tc_cbs(struct rtase_private *tp,
> > +                            const struct tc_cbs_qopt_offload *qopt)
> > +{
> > +     u32 queue = qopt->queue;
> > +
> > +     tp->tx_qos[queue].hicredit = qopt->hicredit;
> > +     tp->tx_qos[queue].locredit = qopt->locredit;
> > +     tp->tx_qos[queue].idleslope = qopt->idleslope;
> > +     tp->tx_qos[queue].sendslope = qopt->sendslope;
> > +
> > +     /* set hardware cbs */
> > +     rtase_set_hw_cbs(tp, queue);
> > +}
> > +
> > +static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type,
> > +                       void *type_data)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     switch (type) {
> > +     case TC_SETUP_QDISC_CBS:
> > +             rtase_setup_tc_cbs(tp, type_data);
> > +             break;
> > +     default:
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static netdev_features_t rtase_fix_features(struct net_device *dev,
> > +                                         netdev_features_t
> features)
> > +{
> > +     netdev_features_t features_fix = features;
> > +
> > +     if (dev->mtu > MSS_MAX)
> > +             features_fix &= ~NETIF_F_ALL_TSO;
> > +
> > +     if (dev->mtu > ETH_DATA_LEN)
> > +             features_fix &= ~NETIF_F_ALL_TSO;
> > +
> > +     return features_fix;
> > +}
> > +
> > +static int rtase_set_features(struct net_device *dev,
> > +                           netdev_features_t features)
> > +{
> > +     netdev_features_t features_set = features;
> > +
> > +     features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM |
> > +                     NETIF_F_HW_VLAN_CTAG_RX;
> > +
> > +     if (features_set ^ dev->features)
> > +             rtase_hw_set_features(dev, features_set);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct net_device_ops rtase_netdev_ops = {
> > +     .ndo_open = rtase_open,
> > +     .ndo_stop = rtase_close,
> > +     .ndo_start_xmit = rtase_start_xmit,
> > +     .ndo_set_rx_mode = rtase_set_rx_mode,
> > +     .ndo_set_mac_address = rtase_set_mac_address,
> > +     .ndo_change_mtu = rtase_change_mtu,
> > +     .ndo_tx_timeout = rtase_tx_timeout,
> > +     .ndo_get_stats64 = rtase_get_stats64,
> > +     .ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
> > +     .ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
> > +#ifdef CONFIG_NET_POLL_CONTROLLER
> > +     .ndo_poll_controller = rtase_netpoll,
> > +#endif
> > +     .ndo_setup_tc = rtase_setup_tc,
> > +     .ndo_fix_features = rtase_fix_features,
> > +     .ndo_set_features = rtase_set_features,
> > +};
> > +
> > +static void rtase_get_mac_address(struct net_device *dev)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     u8 mac_addr[ETH_ALEN] __aligned(2) = {};
> > +     u32 i;
> > +
> > +     for (i = 0; i < ETH_ALEN; i++)
> > +             mac_addr[i] = rtase_r8(tp, RTASE_MAC0 + i);
> > +
> > +     if (!is_valid_ether_addr(mac_addr)) {
> > +             eth_random_addr(mac_addr);
> > +             dev->addr_assign_type = NET_ADDR_RANDOM;
> > +             netdev_warn(dev, "Random ether addr %pM\n",
> mac_addr);
> > +     }
> > +
> > +     eth_hw_addr_set(dev, mac_addr);
> > +     rtase_rar_set(tp, mac_addr);
> > +
> > +     /* keep the original MAC address */
> > +     ether_addr_copy(tp->org_mac_addr, dev->dev_addr);
> > +     ether_addr_copy(dev->perm_addr, dev->dev_addr);
> > +}
> > +
> > +static void rtase_get_drvinfo(struct net_device *dev,
> > +                           struct ethtool_drvinfo *drvinfo)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
> > +     strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> > +}
> > +
> > +static int rtase_get_settings(struct net_device *dev,
> > +                           struct ethtool_link_ksettings *cmd)
> > +{
> > +     u32 supported = SUPPORTED_MII | SUPPORTED_Pause |
> SUPPORTED_Asym_Pause;
> > +
> > +
> ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
> > +                                             supported);
> > +     cmd->base.speed = SPEED_5000;
> > +     cmd->base.duplex = DUPLEX_FULL;
> > +     cmd->base.port = PORT_MII;
> > +     cmd->base.autoneg = AUTONEG_DISABLE;
> > +
> > +     return 0;
> > +}
> > +
> > +static void rtase_get_pauseparam(struct net_device *dev,
> > +                              struct ethtool_pauseparam *pause)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +
> > +     pause->autoneg = AUTONEG_DISABLE;
> > +
> > +     if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
> > +         (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
> > +             pause->rx_pause = 1;
> > +             pause->tx_pause = 1;
> > +     } else if ((value & FORCE_TXFLOW_EN)) {
> > +             pause->tx_pause = 1;
> > +     } else if ((value & FORCE_RXFLOW_EN)) {
> > +             pause->rx_pause = 1;
> > +     }
> > +}
> > +
> > +static int rtase_set_pauseparam(struct net_device *dev,
> > +                             struct ethtool_pauseparam *pause)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +
> > +     if (pause->autoneg)
> > +             return -EOPNOTSUPP;
> > +
> > +     value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> > +
> > +     if (pause->tx_pause)
> > +             value |= FORCE_TXFLOW_EN;
> > +
> > +     if (pause->rx_pause)
> > +             value |= FORCE_RXFLOW_EN;
> > +
> > +     rtase_w16(tp, RTASE_CPLUS_CMD, value);
> > +     return 0;
> > +}
> > +
> > +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> > +     "tx_packets",
> > +     "rx_packets",
> > +     "tx_errors",
> > +     "rx_errors",
> > +     "rx_missed",
> > +     "align_errors",
> > +     "tx_single_collisions",
> > +     "tx_multi_collisions",
> > +     "unicast",
> > +     "broadcast",
> > +     "multicast",
> > +     "tx_aborted",
> > +     "tx_underrun",
> > +};
> > +
> > +static void rtase_get_strings(struct net_device *dev, u32 stringset, u8
> *data)
> > +{
> > +     switch (stringset) {
> > +     case ETH_SS_STATS:
> > +             memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
> > +             break;
> > +     }
> > +}
> > +
> > +static int rtase_get_sset_count(struct net_device *dev, int sset)
> > +{
> > +     int ret = -EOPNOTSUPP;
> > +
> > +     switch (sset) {
> > +     case ETH_SS_STATS:
> > +             ret = ARRAY_SIZE(rtase_gstrings);
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void rtase_get_ethtool_stats(struct net_device *dev,
> > +                                 struct ethtool_stats *stats, u64
> *data)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters;
> > +
> > +     ASSERT_RTNL();
> > +
> > +     counters = tp->tally_vaddr;
> > +     if (!counters)
> > +             return;
> > +
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     data[0] = le64_to_cpu(counters->tx_packets);
> > +     data[1] = le64_to_cpu(counters->rx_packets);
> > +     data[2] = le64_to_cpu(counters->tx_errors);
> > +     data[3] = le32_to_cpu(counters->rx_errors);
> > +     data[4] = le16_to_cpu(counters->rx_missed);
> > +     data[5] = le16_to_cpu(counters->align_errors);
> > +     data[6] = le32_to_cpu(counters->tx_one_collision);
> > +     data[7] = le32_to_cpu(counters->tx_multi_collision);
> > +     data[8] = le64_to_cpu(counters->rx_unicast);
> > +     data[9] = le64_to_cpu(counters->rx_broadcast);
> > +     data[10] = le32_to_cpu(counters->rx_multicast);
> > +     data[11] = le16_to_cpu(counters->tx_aborted);
> > +     data[12] = le16_to_cpu(counters->tx_underun);
> > +}
> > +
> > +static const struct ethtool_ops rtase_ethtool_ops = {
> > +     .get_drvinfo = rtase_get_drvinfo,
> > +     .get_link = ethtool_op_get_link,
> > +     .get_link_ksettings = rtase_get_settings,
> > +     .get_pauseparam = rtase_get_pauseparam,
> > +     .set_pauseparam = rtase_set_pauseparam,
> > +     .get_strings = rtase_get_strings,
> > +     .get_sset_count = rtase_get_sset_count,
> > +     .get_ethtool_stats = rtase_get_ethtool_stats,
> > +     .get_ts_info = ethtool_op_get_ts_info,
> > +};
> > +
> > +static void rtase_init_netdev_ops(struct net_device *dev)
> > +{
> > +     dev->netdev_ops = &rtase_netdev_ops;
> > +     dev->ethtool_ops = &rtase_ethtool_ops;
> > +}
> > +
> > +static void rtase_reset_interrupt(struct pci_dev *pdev,
> > +                               const struct rtase_private *tp)
> > +{
> > +     if (tp->sw_flag & SWF_MSIX_ENABLED)
> > +             pci_disable_msix(pdev);
> > +     else
> > +             pci_disable_msi(pdev);
> > +}
> > +
> > +static int rtase_alloc_msix(struct pci_dev *pdev, struct rtase_private *tp)
> > +{
> > +     int ret;
> > +     u16 i;
> > +
> > +     memset(tp->msix_entry, 0x0, RTASE_NUM_MSIX * sizeof(struct
> msix_entry));
> > +
> > +     for (i = 0; i < RTASE_NUM_MSIX; i++)
> > +             tp->msix_entry[i].entry = i;
> > +
> > +     ret = pci_enable_msix_range(pdev, tp->msix_entry, tp->int_nums,
> > +                                 tp->int_nums);
> > +
> > +     if (ret == tp->int_nums) {
> > +             for (i = 0; i < tp->int_nums; i++) {
> > +                     tp->int_vector[i].irq = pci_irq_vector(pdev, i);
> > +                     tp->int_vector[i].status = 1;
> > +             }
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int rtase_alloc_interrupt(struct pci_dev *pdev,
> > +                              struct rtase_private *tp)
> > +{
> > +     int ret;
> > +
> > +     ret = rtase_alloc_msix(pdev, tp);
> > +     if (ret != tp->int_nums) {
> > +             ret = pci_enable_msi(pdev);
> > +             if (ret)
> > +                     dev_err(&pdev->dev,
> > +                             "unable to alloc interrupt.(MSI)\n");
> > +             else
> > +                     tp->sw_flag |= SWF_MSI_ENABLED;
> > +     } else {
> > +             tp->sw_flag |= SWF_MSIX_ENABLED;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void rtase_init_hardware(const struct rtase_private *tp)
> > +{
> > +     u16 i;
> > +
> > +     for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++)
> > +             rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
> > +}
> > +
> > +static void rtase_init_int_vector(struct rtase_private *tp)
> > +{
> > +     u16 i;
> > +
> > +     /* interrupt vector 0 */
> > +     tp->int_vector[0].tp = tp;
> > +     tp->int_vector[0].index = 0;
> > +     tp->int_vector[0].imr_addr = RTASE_IMR0;
> > +     tp->int_vector[0].isr_addr = RTASE_ISR0;
> > +     tp->int_vector[0].imr = ROK | RDU | TOK | TOK4 | TOK5 | TOK6 |
> TOK7;
> > +     tp->int_vector[0].poll = rtase_poll;
> > +
> > +     memset(tp->int_vector[0].name, 0x0,
> sizeof(tp->int_vector[0].name));
> > +     INIT_LIST_HEAD(&tp->int_vector[0].ring_list);
> > +
> > +     netif_napi_add(tp->dev, &tp->int_vector[0].napi,
> > +                    tp->int_vector[0].poll);
> > +     napi_enable(&tp->int_vector[0].napi);
> > +
> > +     /* interrupt vector 1 ~ 3 */
> > +     for (i = 1; i < tp->int_nums; i++) {
> > +             tp->int_vector[i].tp = tp;
> > +             tp->int_vector[i].index = i;
> > +             tp->int_vector[i].imr_addr = RTASE_IMR1 + (i - 1) * 4;
> > +             tp->int_vector[i].isr_addr = RTASE_ISR1 + (i - 1) * 4;
> > +             tp->int_vector[i].imr = Q_ROK | Q_RDU | Q_TOK;
> > +             tp->int_vector[i].poll = rtase_poll;
> > +
> > +             memset(tp->int_vector[i].name, 0x0,
> sizeof(tp->int_vector[0].name));
> > +             INIT_LIST_HEAD(&tp->int_vector[i].ring_list);
> > +
> > +             netif_napi_add(tp->dev, &tp->int_vector[i].napi,
> > +                            tp->int_vector[i].poll);
> > +             napi_enable(&tp->int_vector[i].napi);
> > +     }
> > +}
> > +
> > +static u16 rtase_calc_time_mitigation(u32 time_us)
> > +{
> > +     u16 int_miti;
> > +     u8 msb, time_count, time_unit;
> > +
> > +     time_us = min_t(int, time_us, MITI_MAX_TIME);
> > +
> > +     msb = fls(time_us);
> > +     if (msb >= MITI_COUNT_BIT_NUM) {
> > +             time_unit = msb - MITI_COUNT_BIT_NUM;
> > +             time_count = time_us >> (msb - MITI_COUNT_BIT_NUM);
> > +     } else {
> > +             time_unit = 0;
> > +             time_count = time_us;
> > +     }
> > +
> > +     int_miti = u16_encode_bits(time_count, MITI_TIME_COUNT_MASK) |
> > +                u16_encode_bits(time_unit, MITI_TIME_UNIT_MASK);
> > +
> > +     return int_miti;
> > +}
> > +
> > +static u16 rtase_calc_packet_num_mitigation(u16 pkt_num)
> > +{
> > +     u16 int_miti;
> > +     u8 msb, pkt_num_count, pkt_num_unit;
> > +
> > +     pkt_num = min_t(int, pkt_num, MITI_MAX_PKT_NUM);
> > +
> > +     if (pkt_num > 60) {
> > +             pkt_num_unit = MITI_MAX_PKT_NUM_IDX;
> > +             pkt_num_count = pkt_num / MITI_MAX_PKT_NUM_UNIT;
> > +     } else {
> > +             msb = fls(pkt_num);
> > +             if (msb >= MITI_COUNT_BIT_NUM) {
> > +                     pkt_num_unit = msb - MITI_COUNT_BIT_NUM;
> > +                     pkt_num_count = pkt_num >> (msb -
> MITI_COUNT_BIT_NUM);
> > +             } else {
> > +                     pkt_num_unit = 0;
> > +                     pkt_num_count = pkt_num;
> > +             }
> > +     }
> > +
> > +     int_miti = u16_encode_bits(pkt_num_count,
> MITI_PKT_NUM_COUNT_MASK) |
> > +                u16_encode_bits(pkt_num_unit,
> MITI_PKT_NUM_UNIT_MASK);
> > +
> > +     return int_miti;
> > +}
> > +
> > +static void rtase_init_software_variable(struct pci_dev *pdev,
> > +                                      struct rtase_private *tp)
> > +{
> > +     u16 int_miti;
> > +
> > +     tp->tx_queue_ctrl = RTASE_TXQ_CTRL;
> > +     tp->func_tx_queue_num = RTASE_FUNC_TXQ_NUM;
> > +     tp->func_rx_queue_num = RTASE_FUNC_RXQ_NUM;
> > +     tp->int_nums = RTASE_INTERRUPT_NUM;
> > +
> > +     int_miti = rtase_calc_time_mitigation(MITI_DEFAULT_TIME) |
> > +
> rtase_calc_packet_num_mitigation(MITI_DEFAULT_PKT_NUM);
> > +     tp->tx_int_mit = int_miti;
> > +     tp->rx_int_mit = int_miti;
> > +
> > +     tp->sw_flag = 0;
> > +
> > +     rtase_init_int_vector(tp);
> > +
> > +     /* MTU range: 60 - hw-specific max */
> > +     tp->dev->min_mtu = ETH_ZLEN;
> > +     tp->dev->max_mtu = MAX_JUMBO_SIZE;
> > +}
> > +
> > +static bool rtase_check_mac_version_valid(struct rtase_private *tp)
> > +{
> > +     u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & HW_VER_MASK;
> > +     bool known_ver = false;
> > +
> > +     switch (hw_ver) {
> > +     case 0x00800000:
> > +     case 0x04000000:
> > +     case 0x04800000:
> > +             known_ver = true;
> > +             break;
> > +     }
> > +
> > +     return known_ver;
> > +}
> > +
> > +static int rtase_init_board(struct pci_dev *pdev, struct net_device
> **dev_out,
> > +                         void __iomem **ioaddr_out)
> > +{
> > +     struct net_device *dev;
> > +     void __iomem *ioaddr;
> > +     int ret = -ENOMEM;
> > +
> > +     /* dev zeroed in alloc_etherdev */
> > +     dev = alloc_etherdev_mq(sizeof(struct rtase_private),
> > +                             RTASE_FUNC_TXQ_NUM);
> > +     if (!dev)
> > +             goto err_out;
> > +
> > +     SET_NETDEV_DEV(dev, &pdev->dev);
> > +
> > +     ret = pci_enable_device(pdev);
> > +     if (ret < 0)
> > +             goto err_out_free_dev;
> > +
> > +     /* make sure PCI base addr 1 is MMIO */
> > +     if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> > +             ret = -ENODEV;
> > +             goto err_out_disable;
> > +     }
> > +
> > +     /* check for weird/broken PCI region reporting */
> > +     if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
> > +             ret = -ENODEV;
> > +             goto err_out_disable;
> > +     }
> > +
> > +     ret = pci_request_regions(pdev, KBUILD_MODNAME);
> > +     if (ret < 0)
> > +             goto err_out_disable;
> > +
> > +     if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
> > +             dev->features |= NETIF_F_HIGHDMA;
> > +     else if (dma_set_mask_and_coherent(&pdev->dev,
> DMA_BIT_MASK(32)))
> > +             goto err_out_free_res;
> > +     else
> > +             dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
> > +
> > +     pci_set_master(pdev);
> > +
> > +     /* ioremap MMIO region */
> > +     ioaddr = ioremap(pci_resource_start(pdev, 2),
> > +                      pci_resource_len(pdev, 2));
> > +     if (!ioaddr) {
> > +             ret = -EIO;
> > +             goto err_out_free_res;
> > +     }
> > +
> > +     *ioaddr_out = ioaddr;
> > +     *dev_out = dev;
> > +     goto out;
> > +
> > +err_out_free_res:
> > +     pci_release_regions(pdev);
> > +
> > +err_out_disable:
> > +     pci_disable_device(pdev);
> > +
> > +err_out_free_dev:
> > +     free_netdev(dev);
> > +
> > +err_out:
> > +     *ioaddr_out = NULL;
> > +     *dev_out = NULL;
> > +
> > +out:
> > +     return ret;
> > +}
> > +
> > +static void rtase_release_board(struct pci_dev *pdev, struct net_device
> *dev,
> > +                             void __iomem *ioaddr)
> > +{
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     rtase_rar_set(tp, tp->org_mac_addr);
> > +     iounmap(ioaddr);
> > +
> > +     if ((tp->sw_flag & SWF_MSIX_ENABLED))
> > +             pci_disable_msix(pdev);
> > +     else
> > +             pci_disable_msi(pdev);
> > +
> > +     pci_release_regions(pdev);
> > +     pci_disable_device(pdev);
> > +     free_netdev(dev);
> > +}
> > +
> > +static int rtase_init_one(struct pci_dev *pdev,
> > +                       const struct pci_device_id *ent)
> > +{
> > +     struct net_device *dev = NULL;
> > +     void __iomem *ioaddr = NULL;
> > +     struct rtase_private *tp;
> > +     int ret;
> > +
> > +     if (!pdev->is_physfn && pdev->is_virtfn) {
> > +             dev_err(&pdev->dev, "This module does not support a
> virtual function.");
> > +             return -EINVAL;
> > +     }
> > +
> > +     dev_dbg(&pdev->dev, "Automotive Switch Ethernet driver loaded\n");
> > +
> > +     ret = rtase_init_board(pdev, &dev, &ioaddr);
> > +     if (ret != 0)
> > +             return ret;
> > +
> > +     tp = netdev_priv(dev);
> > +     tp->mmio_addr = ioaddr;
> > +     tp->dev = dev;
> > +     tp->pdev = pdev;
> > +
> > +     /* identify chip attached to board */
> > +     if (!rtase_check_mac_version_valid(tp)) {
> > +             return dev_err_probe(&pdev->dev, -ENODEV,
> > +                                  "unknown chip version, contact
> rtase maintainers (see MAINTAINERS file)\n");
> > +     }
> > +
> > +     dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
> > +     if (!dev->tstats)
> > +             goto err_out_1;
> > +
> > +     rtase_init_software_variable(pdev, tp);
> > +     rtase_init_hardware(tp);
> > +
> > +     ret = rtase_alloc_interrupt(pdev, tp);
> > +     if (ret < 0) {
> > +             dev_err(&pdev->dev, "unable to alloc MSIX/MSI\n");
> > +             goto err_out_1;
> > +     }
> > +
> > +     rtase_init_netdev_ops(dev);
> > +
> > +     dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
> NETIF_F_HW_VLAN_CTAG_RX;
> > +
> > +     dev->features |= NETIF_F_IP_CSUM;
> > +     dev->features |= NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
> > +     dev->features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
> > +     dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO
> |
> > +                        NETIF_F_RXCSUM |
> NETIF_F_HW_VLAN_CTAG_TX |
> > +                        NETIF_F_HW_VLAN_CTAG_RX;
> > +     dev->hw_features |= NETIF_F_RXALL;
> > +     dev->hw_features |= NETIF_F_RXFCS;
> > +     dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
> > +     dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO
> |
> > +                          NETIF_F_HIGHDMA;
> > +     dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
> > +     netif_set_tso_max_size(dev, LSO_64K);
> > +     netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
> > +
> > +     rtase_get_mac_address(dev);
> > +
> > +     tp->tally_vaddr = dma_alloc_coherent(&pdev->dev,
> > +
> sizeof(*tp->tally_vaddr),
> > +                                          &tp->tally_paddr,
> > +                                          GFP_KERNEL);
> > +     if (!tp->tally_vaddr) {
> > +             ret = -ENOMEM;
> > +             goto err_out;
> > +     }
> > +
> > +     rtase_tally_counter_clear(tp);
> > +
> > +     pci_set_drvdata(pdev, dev);
> > +
> > +     ret = register_netdev(dev);
> > +     if (ret != 0)
> > +             goto err_out;
> > +
> > +     netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq);
> > +
> > +     netif_carrier_off(dev);
> > +
> > +     goto out;
> > +
> > +err_out:
> > +     if (tp->tally_vaddr) {
> > +             dma_free_coherent(&pdev->dev,
> > +                               sizeof(*tp->tally_vaddr),
> > +                               tp->tally_vaddr,
> > +                               tp->tally_paddr);
> > +
> > +             tp->tally_vaddr = NULL;
> > +     }
> > +
> > +err_out_1:
> > +     rtase_release_board(pdev, dev, ioaddr);
> > +
> > +out:
> > +     return ret;
> > +}
> > +
> > +static void rtase_remove_one(struct pci_dev *pdev)
> > +{
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     struct rtase_int_vector *ivec;
> > +     u32 i;
> > +
> > +     for (i = 0; i < tp->int_nums; i++) {
> > +             ivec = &tp->int_vector[i];
> > +             netif_napi_del(&ivec->napi);
> > +     }
> > +
> > +     unregister_netdev(dev);
> > +     rtase_reset_interrupt(pdev, tp);
> > +     if (tp->tally_vaddr) {
> > +             dma_free_coherent(&pdev->dev,
> > +                               sizeof(*tp->tally_vaddr),
> > +                               tp->tally_vaddr,
> > +                               tp->tally_paddr);
> > +             tp->tally_vaddr = NULL;
> > +     }
> > +
> > +     rtase_release_board(pdev, dev, tp->mmio_addr);
> > +     pci_set_drvdata(pdev, NULL);
> > +}
> > +
> > +static void rtase_shutdown(struct pci_dev *pdev)
> > +{
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     if (netif_running(dev))
> > +             rtase_close(dev);
> > +
> > +     rtase_reset_interrupt(pdev, tp);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
> > +{
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +
> > +     if (!netif_running(dev))
> > +             goto out;
> > +
> > +     netif_stop_queue(dev);
> > +     netif_carrier_off(dev);
> > +     netif_device_detach(dev);
> > +     rtase_hw_reset(dev);
> > +
> > +out:
> > +     pci_save_state(pdev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rtase_resume(struct pci_dev *pdev)
> > +{
> > +     struct net_device *dev = pci_get_drvdata(pdev);
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     int ret;
> > +
> > +     pci_set_power_state(pdev, PCI_D0);
> > +     pci_restore_state(pdev);
> > +     pci_enable_wake(pdev, PCI_D0, 0);
> > +
> > +     /* restore last modified mac address */
> > +     rtase_rar_set(tp, dev->dev_addr);
> > +
> > +     if (!netif_running(dev))
> > +             goto out;
> > +
> > +     rtase_wait_for_quiescence(dev);
> > +
> > +     rtase_tx_clear(tp);
> > +     rtase_rx_clear(tp);
> > +
> > +     ret = rtase_init_ring(dev);
> > +     if (ret) {
> > +             netdev_err(dev, "unable to init ring\n");
> > +             rtase_free_desc(tp);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     rtase_hw_config(dev);
> > +     /* always link, so start to transmit & receive */
> > +     rtase_hw_start(dev);
> > +
> > +     netif_carrier_on(dev);
> > +     netif_wake_queue(dev);
> > +     netif_device_attach(dev);
> > +
> > +out:
> > +     return 0;
> > +}
> > +#endif /* CONFIG_PM */
> > +
> > +static struct pci_driver rtase_pci_driver = {
> > +     .name = KBUILD_MODNAME,
> > +     .id_table = rtase_pci_tbl,
> > +     .probe = rtase_init_one,
> > +     .remove = rtase_remove_one,
> > +     .shutdown = rtase_shutdown,
> > +#ifdef CONFIG_PM
> > +     .suspend = rtase_suspend,
> > +     .resume = rtase_resume,
> > +#endif
> > +};
> > +
> > +module_pci_driver(rtase_pci_driver);
> > diff --git a/drivers/net/ethernet/realtek/rtase/tt.h
> b/drivers/net/ethernet/realtek/rtase/tt.h
> > new file mode 100644
> > index 000000000000..9239c518c504
> > --- /dev/null
> > +++ b/drivers/net/ethernet/realtek/rtase/tt.h
> > @@ -0,0 +1,353 @@
> > +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> > +/*
> > + *  rtase is the Linux device driver released for Realtek Automotive Switch
> > + *  controllers with PCI-Express interface.
> > + *
> > + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> > + */
> > +
> > +#ifndef _RTASE_H_
> > +#define _RTASE_H_
> > +
> > +/* the low 32 bit address of receive buffer must be 8-byte alignment. */
> > +#define RTK_RX_ALIGN 8
> > +
> > +#define HW_VER_MASK 0x7C800000
> > +
> > +#define RX_DMA_BURST_256       4
> > +#define TX_DMA_BURST_UNLIMITED 7
> > +#define RX_BUF_SIZE            (PAGE_SIZE - \
> > +                             SKB_DATA_ALIGN(sizeof(struct
> skb_shared_info)))
> > +#define MAX_JUMBO_SIZE         (RX_BUF_SIZE - VLAN_ETH_HLEN -
> ETH_FCS_LEN)
> > +
> > +/* 3 means InterFrameGap = the shortest one */
> > +#define INTERFRAMEGAP 0x03
> > +
> > +#define RTASE_REGS_SIZE     256
> > +#define RTASE_PCI_REGS_SIZE 0x100
> > +
> > +#define MULTICAST_FILTER_MASK  GENMASK(30, 26)
> > +#define MULTICAST_FILTER_LIMIT 32
> > +
> > +#define RTASE_VLAN_FILTER_ENTRY_NUM 32
> > +#define RTASE_NUM_TX_QUEUE 8
> > +#define RTASE_NUM_RX_QUEUE 4
> > +
> > +#define RTASE_TXQ_CTRL      1
> > +#define RTASE_FUNC_TXQ_NUM  1
> > +#define RTASE_FUNC_RXQ_NUM  1
> > +#define RTASE_INTERRUPT_NUM 1
> > +
> > +#define MITI_TIME_COUNT_MASK     GENMASK(3, 0)
> > +#define MITI_TIME_UNIT_MASK      GENMASK(7, 4)
> > +#define MITI_DEFAULT_TIME        128
> > +#define MITI_MAX_TIME            491520
> > +#define MITI_PKT_NUM_COUNT_MASK  GENMASK(11, 8)
> > +#define MITI_PKT_NUM_UNIT_MASK   GENMASK(13, 12)
> > +#define MITI_DEFAULT_PKT_NUM     64
> > +#define MITI_MAX_PKT_NUM_IDX     3
> > +#define MITI_MAX_PKT_NUM_UNIT    16
> > +#define MITI_MAX_PKT_NUM         240
> > +#define MITI_COUNT_BIT_NUM       4
> > +
> > +#define RTASE_NUM_MSIX 4
> > +
> > +#define RTASE_DWORD_MOD 16
> > +
> >
> +/**************************************************************
> ***************/
> > +enum rtase_registers {
> > +     RTASE_MAC0   = 0x0000,
> > +     RTASE_MAC4   = 0x0004,
> > +     RTASE_MAR0   = 0x0008,
> > +     RTASE_MAR1   = 0x000C,
> > +     RTASE_DTCCR0 = 0x0010,
> > +     RTASE_DTCCR4 = 0x0014,
> > +#define COUNTER_RESET BIT(0)
> > +#define COUNTER_DUMP  BIT(3)
> > +
> > +     RTASE_FCR    = 0x0018,
> > +#define FCR_RXQ_MASK    GENMASK(5, 4)
> > +#define FCR_VLAN_FTR_EN BIT(1)
> > +
> > +     RTASE_LBK_CTRL = 0x001A,
> > +#define LBK_ATLD BIT(1)
> > +#define LBK_CLR  BIT(0)
> > +
> > +     RTASE_TX_DESC_ADDR0   = 0x0020,
> > +     RTASE_TX_DESC_ADDR4   = 0x0024,
> > +     RTASE_TX_DESC_COMMAND = 0x0028,
> > +#define TX_DESC_CMD_CS BIT(15)
> > +#define TX_DESC_CMD_WE BIT(14)
> > +
> > +     RTASE_BOOT_CTL  = 0x6004,
> > +     RTASE_CLKSW_SET = 0x6018,
> > +
> > +     RTASE_CHIP_CMD = 0x0037,
> > +#define STOP_REQ      BIT(7)
> > +#define STOP_REQ_DONE BIT(6)
> > +#define RE            BIT(3)
> > +#define TE            BIT(2)
> > +
> > +     RTASE_IMR0 = 0x0038,
> > +     RTASE_ISR0 = 0x003C,
> > +#define TOK7 BIT(30)
> > +#define TOK6 BIT(28)
> > +#define TOK5 BIT(26)
> > +#define TOK4 BIT(24)
> > +#define FOVW BIT(6)
> > +#define RDU  BIT(4)
> > +#define TOK  BIT(2)
> > +#define ROK  BIT(0)
> > +
> > +     RTASE_IMR1 = 0x0800,
> > +     RTASE_ISR1 = 0x0802,
> > +#define Q_TOK BIT(4)
> > +#define Q_RDU BIT(1)
> > +#define Q_ROK BIT(0)
> > +
> > +     RTASE_EPHY_ISR = 0x6014,
> > +     RTASE_EPHY_IMR = 0x6016,
> > +
> > +     RTASE_TX_CONFIG_0 = 0x0040,
> > +#define TX_INTER_FRAME_GAP_MASK GENMASK(25, 24)
> > +     /* DMA burst value (0-7) is shift this many bits */
> > +#define TX_DMA_MASK             GENMASK(10, 8)
> > +
> > +     RTASE_RX_CONFIG_0 = 0x0044,
> > +#define RX_SINGLE_FETCH  BIT(14)
> > +#define RX_SINGLE_TAG    BIT(13)
> > +#define RX_MX_DMA_MASK   GENMASK(10, 8)
> > +#define ACPT_FLOW        BIT(7)
> > +#define ACCEPT_ERR       BIT(5)
> > +#define ACCEPT_RUNT      BIT(4)
> > +#define ACCEPT_BROADCAST BIT(3)
> > +#define ACCEPT_MULTICAST BIT(2)
> > +#define ACCEPT_MYPHYS    BIT(1)
> > +#define ACCEPT_ALLPHYS   BIT(0)
> > +#define ACCEPT_MASK      (ACPT_FLOW | ACCEPT_ERR | ACCEPT_RUNT
> | \
> > +                       ACCEPT_BROADCAST | ACCEPT_MULTICAST | \
> > +                       ACCEPT_MYPHYS | ACCEPT_ALLPHYS)
> > +
> > +     RTASE_RX_CONFIG_1 = 0x0046,
> > +#define RX_MAX_FETCH_DESC_MASK  GENMASK(15, 11)
> > +#define RX_NEW_DESC_FORMAT_EN   BIT(8)
> > +#define OUTER_VLAN_DETAG_EN     BIT(7)
> > +#define INNER_VLAN_DETAG_EN     BIT(6)
> > +#define PCIE_NEW_FLOW           BIT(2)
> > +#define PCIE_RELOAD_En          BIT(0)
> > +
> > +     RTASE_EEM = 0x0050,
> > +#define EEM_UNLOCK 0xC0
> > +
> > +     RTASE_TDFNR  = 0x0057,
> > +     RTASE_TPPOLL = 0x0090,
> > +     RTASE_PDR    = 0x00B0,
> > +     RTASE_FIFOR  = 0x00D3,
> > +#define TX_FIFO_EMPTY BIT(5)
> > +#define RX_FIFO_EMPTY BIT(4)
> > +
> > +     RTASE_PCPR = 0x00D8,
> > +#define PCPR_VLAN_FTR_EN BIT(6)
> > +
> > +     RTASE_RMS       = 0x00DA,
> > +     RTASE_CPLUS_CMD = 0x00E0,
> > +#define FORCE_RXFLOW_EN BIT(11)
> > +#define FORCE_TXFLOW_EN BIT(10)
> > +#define RX_CHKSUM       BIT(5)
> > +
> > +     RTASE_Q0_RX_DESC_ADDR0 = 0x00E4,
> > +     RTASE_Q0_RX_DESC_ADDR4 = 0x00E8,
> > +     RTASE_Q1_RX_DESC_ADDR0 = 0x4000,
> > +     RTASE_Q1_RX_DESC_ADDR4 = 0x4004,
> > +     RTASE_MTPS             = 0x00EC,
> > +#define TAG_NUM_SEL_MASK  GENMASK(10, 8)
> > +
> > +     RTASE_MISC = 0x00F2,
> > +#define RX_DV_GATE_EN BIT(3)
> > +
> > +     RTASE_TFUN_CTRL = 0x0400,
> > +#define TX_NEW_DESC_FORMAT_EN BIT(0)
> > +
> > +     RTASE_TX_CONFIG_1 = 0x203E,
> > +#define TC_MODE_MASK  GENMASK(11, 10)
> > +
> > +     RTASE_TOKSEL      = 0x2046,
> > +     RTASE_TXQCRDT_0   = 0x2500,
> > +     RTASE_RFIFONFULL  = 0x4406,
> > +     RTASE_INT_MITI_TX = 0x0A00,
> > +     RTASE_INT_MITI_RX = 0x0A80,
> > +
> > +     RTASE_VLAN_ENTRY_MEM_0 = 0x7234,
> > +     RTASE_VLAN_ENTRY_0     = 0xAC80,
> > +};
> > +
> > +enum desc_status_bit {
> > +     DESC_OWN = BIT(31), /* Descriptor is owned by NIC */
> > +     RING_END = BIT(30), /* End of descriptor ring */
> > +};
> > +
> > +enum sw_flag_content {
> > +     SWF_MSI_ENABLED  = BIT(1),
> > +     SWF_MSIX_ENABLED = BIT(2),
> > +};
> > +
> > +#define RSVD_MASK 0x3FFFC000
> > +
> > +struct tx_desc {
> > +     __le32 opts1;
> > +     __le32 opts2;
> > +     __le64 addr;
> > +     __le32 opts3;
> > +     __le32 reserved1;
> > +     __le32 reserved2;
> > +     __le32 reserved3;
> > +} __packed;
> > +
> > +/*------ offset 0 of tx descriptor ------*/
> > +#define TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */
> > +#define TX_LAST_FRAG  BIT(28) /* Tx Final segment of a packet */
> > +#define GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */
> > +#define GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */
> > +#define TX_VLAN_TAG   BIT(17) /* Add VLAN tag */
> > +
> > +/*------ offset 4 of tx descriptor ------*/
> > +#define TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */
> > +#define TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */
> > +#define TX_IPCS_C  BIT(29) /* Calculate IP checksum */
> > +#define TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */
> > +
> > +union rx_desc {
> > +     struct {
> > +             __le64 header_buf_addr;
> > +             __le32 reserved1;
> > +             __le32 opts_header_len;
> > +             __le64 addr;
> > +             __le32 reserved2;
> > +             __le32 opts1;
> > +     } __packed desc_cmd;
> > +
> > +     struct {
> > +             __le32 reserved1;
> > +             __le32 reserved2;
> > +             __le32 rss;
> > +             __le32 opts4;
> > +             __le32 reserved3;
> > +             __le32 opts3;
> > +             __le32 opts2;
> > +             __le32 opts1;
> > +     } __packed desc_status;
> > +} __packed;
> > +
> > +/*------ offset 28 of rx descriptor ------*/
> > +#define RX_FIRST_FRAG    BIT(25) /* Rx First segment of a packet */
> > +#define RX_LAST_FRAG     BIT(24) /* Rx Final segment of a packet */
> > +#define RX_RES           BIT(20)
> > +#define RX_RUNT          BIT(19)
> > +#define RX_RWT           BIT(18)
> > +#define RX_CRC           BIT(16)
> > +#define RX_V6F           BIT(31)
> > +#define RX_V4F           BIT(30)
> > +#define RX_UDPT          BIT(29)
> > +#define RX_TCPT          BIT(28)
> > +#define RX_IPF           BIT(26) /* IP checksum failed */
> > +#define RX_UDPF          BIT(25) /* UDP/IP checksum failed */
> > +#define RX_TCPF          BIT(24) /* TCP/IP checksum failed */
> > +#define RX_LBK_FIFO_FULL BIT(17) /* Loopback FIFO Full */
> > +#define RX_VLAN_TAG      BIT(16) /* VLAN tag available */
> > +
> > +#define NUM_DESC                1024
> > +#define RTASE_TX_RING_DESC_SIZE (NUM_DESC * sizeof(struct tx_desc))
> > +#define RTASE_RX_RING_DESC_SIZE (NUM_DESC * sizeof(union rx_desc))
> > +#define VLAN_ENTRY_CAREBIT      0xF0000000
> > +#define VLAN_TAG_MASK           GENMASK(15, 0)
> > +#define RX_PKT_SIZE_MASK        GENMASK(13, 0)
> > +
> > +/* txqos hardware definitions */
> > +#define RTASE_1T_CLOCK            64
> > +#define RTASE_1T_POWER            10000000
> > +#define RTASE_IDLESLOPE_INT_SHIFT 25
> > +#define RTASE_IDLESLOPE_INT_MASK  GENMASK(31, 25)
> > +
> > +#define IVEC_NAME_SIZE (IFNAMSIZ + 10)
> > +
> > +struct rtase_int_vector {
> > +     struct rtase_private *tp;
> > +     unsigned int irq;
> > +     u8 status;
> > +     char name[IVEC_NAME_SIZE];
> > +     u16 index;
> > +     u16 imr_addr;
> > +     u16 isr_addr;
> > +     u32 imr;
> > +     struct list_head ring_list;
> > +     struct napi_struct napi;
> > +     int (*poll)(struct napi_struct *napi, int budget);
> > +};
> > +
> > +struct rtase_ring {
> > +     struct rtase_int_vector *ivec;
> > +     void *desc;
> > +     dma_addr_t phy_addr;
> > +     u32 cur_idx;
> > +     u32 dirty_idx;
> > +     u16 index;
> > +
> > +     struct sk_buff *skbuff[NUM_DESC];
> > +     union {
> > +             u32 len[NUM_DESC];
> > +             dma_addr_t data_phy_addr[NUM_DESC];
> > +     } mis;
> > +
> > +     struct list_head ring_entry;
> > +     int (*ring_handler)(struct rtase_ring *ring, int budget);
> > +};
> > +
> > +struct rtase_txqos {
> > +     int hicredit;
> > +     int locredit;
> > +     int idleslope;
> > +     int sendslope;
> > +};
> > +
> > +struct rtase_private {
> > +     void __iomem *mmio_addr;
> > +     u32 sw_flag;
> > +     u32 mc_filter[2];
> > +
> > +     struct pci_dev *pdev;
> > +     struct net_device *dev;
> > +     u32 rx_buf_sz;
> > +
> > +     struct page_pool *page_pool;
> > +     struct rtase_ring tx_ring[RTASE_NUM_TX_QUEUE];
> > +     struct rtase_txqos tx_qos[RTASE_NUM_TX_QUEUE];
> > +     struct rtase_ring rx_ring[RTASE_NUM_RX_QUEUE];
> > +     struct rtase_counters *tally_vaddr;
> > +     dma_addr_t tally_paddr;
> > +
> > +     u32 vlan_filter_ctrl;
> > +     u16 vlan_filter_vid[RTASE_VLAN_FILTER_ENTRY_NUM];
> > +
> > +     struct delayed_work task;
> 
> I don't see this member anywhere used. What is it good for?
> And related: Why the delayed version?
> 
> > +     u8 org_mac_addr[ETH_ALEN];
> 
> How's the org_mac_addr different from perm_addr?

Task and org_mac_addr are redundant codes, I will remove them.
> 
> > +     struct msix_entry msix_entry[RTASE_NUM_MSIX];
> > +     struct rtase_int_vector int_vector[RTASE_NUM_MSIX];
> > +
> > +     u16 tx_queue_ctrl;
> > +     u16 func_tx_queue_num;
> > +     u16 func_rx_queue_num;
> > +     u16 int_nums;
> > +     u16 tx_int_mit;
> > +     u16 rx_int_mit;
> > +};
> > +
> > +#define LSO_64K 64000
> > +
> > +#define NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4)
> > +
> > +#define TCPHO_MASK GENMASK(24, 18)
> > +
> > +#define MSS_MAX  0x07FF /* MSS value */
> > +#define MSS_MASK GENMASK(28, 18)
> > +
> > +#endif /* _RTASE_H_ */
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function
  2023-11-15 15:02   ` Heiner Kallweit
@ 2023-11-23  3:16     ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-23  3:16 UTC (permalink / raw)
  To: Heiner Kallweit, kuba
  Cc: davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On 15.11.2023 14:34, Justin Lai wrote:
> > Implement the ethtool function to support users to obtain network card
> > information, including obtaining various device settings, Report
> > whether physical link is up, Report pause parameters, Set pause
> > parameters, Return a set of strings that describe the requested
> > objects, Get number of strings that @get_strings will write, Return
> > extended statistics about the device.
> >
> > Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> > ---
> >  .../net/ethernet/realtek/rtase/rtase_main.c   | 144 ++++++++++++++++++
> >  1 file changed, 144 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > index b7679b74cc8a..5ea4d51fcc47 100644
> > --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> > @@ -1900,9 +1900,153 @@ static void rtase_get_mac_address(struct
> net_device *dev)
> >       ether_addr_copy(dev->perm_addr, dev->dev_addr);  }
> >
> > +static void rtase_get_drvinfo(struct net_device *dev,
> > +                           struct ethtool_drvinfo *drvinfo) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
> > +     strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32); }
> > +
> > +static int rtase_get_settings(struct net_device *dev,
> > +                           struct ethtool_link_ksettings *cmd) {
> > +     u32 supported = SUPPORTED_MII | SUPPORTED_Pause |
> > +SUPPORTED_Asym_Pause;
> > +
> > +
> ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
> > +                                             supported);
> > +     cmd->base.speed = SPEED_5000;
> > +     cmd->base.duplex = DUPLEX_FULL;
> > +     cmd->base.port = PORT_MII;
> > +     cmd->base.autoneg = AUTONEG_DISABLE;
> > +
> What are you reporting here? Does this refer to the link between MAC and
> switch CPU port? Because I would assume that the switch ports can do
> autoneg.

PATCH 1/13 has our CHIP architecture. The mac and switch are directly connected, and the line seed is fixed at 5G, so there is no need for autoneg.
> 
> > +     return 0;
> > +}
> > +
> > +static void rtase_get_pauseparam(struct net_device *dev,
> > +                              struct ethtool_pauseparam *pause) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +
> > +     pause->autoneg = AUTONEG_DISABLE;
> > +
> > +     if ((value & (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) ==
> > +         (FORCE_TXFLOW_EN | FORCE_RXFLOW_EN)) {
> > +             pause->rx_pause = 1;
> > +             pause->tx_pause = 1;
> > +     } else if ((value & FORCE_TXFLOW_EN)) {
> > +             pause->tx_pause = 1;
> > +     } else if ((value & FORCE_RXFLOW_EN)) {
> > +             pause->rx_pause = 1;
> > +     }
> > +}
> > +
> > +static int rtase_set_pauseparam(struct net_device *dev,
> > +                             struct ethtool_pauseparam *pause) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +
> > +     if (pause->autoneg)
> > +             return -EOPNOTSUPP;
> > +
> > +     value &= ~(FORCE_TXFLOW_EN | FORCE_RXFLOW_EN);
> > +
> > +     if (pause->tx_pause)
> > +             value |= FORCE_TXFLOW_EN;
> > +
> > +     if (pause->rx_pause)
> > +             value |= FORCE_RXFLOW_EN;
> > +
> > +     rtase_w16(tp, RTASE_CPLUS_CMD, value);
> > +     return 0;
> > +}
> > +
> > +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> > +     "tx_packets",
> > +     "rx_packets",
> > +     "tx_errors",
> > +     "rx_errors",
> > +     "rx_missed",
> > +     "align_errors",
> > +     "tx_single_collisions",
> > +     "tx_multi_collisions",
> > +     "unicast",
> > +     "broadcast",
> > +     "multicast",
> > +     "tx_aborted",
> > +     "tx_underrun",
> > +};
> > +
> > +static void rtase_get_strings(struct net_device *dev, u32 stringset,
> > +u8 *data) {
> > +     switch (stringset) {
> > +     case ETH_SS_STATS:
> > +             memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
> > +             break;
> > +     }
> > +}
> > +
> > +static int rtase_get_sset_count(struct net_device *dev, int sset) {
> > +     int ret = -EOPNOTSUPP;
> > +
> > +     switch (sset) {
> > +     case ETH_SS_STATS:
> > +             ret = ARRAY_SIZE(rtase_gstrings);
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void rtase_get_ethtool_stats(struct net_device *dev,
> > +                                 struct ethtool_stats *stats, u64
> > +*data) {
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters;
> > +
> > +     ASSERT_RTNL();
> > +
> > +     counters = tp->tally_vaddr;
> > +     if (!counters)
> > +             return;
> > +
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     data[0] = le64_to_cpu(counters->tx_packets);
> > +     data[1] = le64_to_cpu(counters->rx_packets);
> > +     data[2] = le64_to_cpu(counters->tx_errors);
> > +     data[3] = le32_to_cpu(counters->rx_errors);
> > +     data[4] = le16_to_cpu(counters->rx_missed);
> > +     data[5] = le16_to_cpu(counters->align_errors);
> > +     data[6] = le32_to_cpu(counters->tx_one_collision);
> > +     data[7] = le32_to_cpu(counters->tx_multi_collision);
> > +     data[8] = le64_to_cpu(counters->rx_unicast);
> > +     data[9] = le64_to_cpu(counters->rx_broadcast);
> > +     data[10] = le32_to_cpu(counters->rx_multicast);
> > +     data[11] = le16_to_cpu(counters->tx_aborted);
> > +     data[12] = le16_to_cpu(counters->tx_underun);
> > +}
> > +
> > +static const struct ethtool_ops rtase_ethtool_ops = {
> > +     .get_drvinfo = rtase_get_drvinfo,
> > +     .get_link = ethtool_op_get_link,
> > +     .get_link_ksettings = rtase_get_settings,
> > +     .get_pauseparam = rtase_get_pauseparam,
> > +     .set_pauseparam = rtase_set_pauseparam,
> > +     .get_strings = rtase_get_strings,
> > +     .get_sset_count = rtase_get_sset_count,
> > +     .get_ethtool_stats = rtase_get_ethtool_stats,
> > +     .get_ts_info = ethtool_op_get_ts_info, };
> > +
> >  static void rtase_init_netdev_ops(struct net_device *dev)  {
> >       dev->netdev_ops = &rtase_netdev_ops;
> > +     dev->ethtool_ops = &rtase_ethtool_ops;
> >  }
> >
> >  static void rtase_reset_interrupt(struct pci_dev *pdev,
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function
  2023-11-16 18:06   ` Simon Horman
@ 2023-11-23  3:48     ` Justin Lai
  0 siblings, 0 replies; 28+ messages in thread
From: Justin Lai @ 2023-11-23  3:48 UTC (permalink / raw)
  To: Simon Horman
  Cc: kuba, davem, edumazet, pabeni, linux-kernel, netdev, andrew,
	Ping-Ke Shih, Larry Chiu

> On Wed, Nov 15, 2023 at 09:34:06PM +0800, Justin Lai wrote:
> > Implement rtase_hw_config to set default hardware settings, including
> > setting interrupt mitigation, tx/rx DMA burst, interframe gap time, rx
> > packet filter, near fifo threshold and fill descriptor ring and tally
> > counter address, and enable flow control. When filling the rx
> > descriptor ring, the first group of queues needs to be processed
> > separately because the positions of the first group of queues are not
> > regular with other subsequent groups. The other queues are all newly
> > added features, but we want to retain the original design. So they
> > were not put together.
> >
> > Signed-off-by: Justin Lai <justinlai0215@realtek.com>
> 
> Hi Justin,
> 
> some minor feedback from my side.
> 
> * I think that "rtase: " would be a more appropriate prefix
>   for the patches in this patch-set.
> 
>   Subject: [PATCH net-next vX m/n] rtase: ...

Ok, I will modify this part
> 
> ...
> 
> > diff --git a/drivers/net/ethernet/realtek/rtase/tt.c
> > b/drivers/net/ethernet/realtek/rtase/tt.c
> 
> ...
> 
> > new file mode 100644
> > index 000000000000..5ea4d51fcc47
> > --- /dev/null
> > +++ b/drivers/net/ethernet/realtek/rtase/tt.c
> 
> ...
> 
> > +static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start,
> > +u32 n) {
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     struct net_device *dev = tp->dev;
> > +     struct tx_desc *desc_base = ring->desc;
> > +     u32 i;
> 
> nit: Please consider using reverse xmas tree - longest line to shortest -
>      for new Networking code.

Thank you for your review, I will modify it and check if there are other similar issues
> 
> ...
> 
> > +static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device
> > +*dev) {
> > +     u8 ip_protocol;
> > +     u32 csum_cmd;
> > +
> > +     switch (vlan_get_protocol(skb)) {
> > +     case htons(ETH_P_IP):
> > +             csum_cmd = TX_IPCS_C;
> > +             ip_protocol = ip_hdr(skb)->protocol;
> > +             break;
> > +
> > +     case htons(ETH_P_IPV6):
> > +             csum_cmd = TX_IPV6F_C;
> > +             ip_protocol = ipv6_hdr(skb)->nexthdr;
> > +             break;
> > +
> > +     default:
> > +             ip_protocol = IPPROTO_RAW;
> > +             break;
> 
> If the default branch is taken in this switch statement, then csum_cmd is used
> uninitialised below.
> 
> As flagged by a clang-16 W=1 build

Thanks, I will fix this compiler warning
> 
> > +     }
> > +
> > +     if (ip_protocol == IPPROTO_TCP)
> > +             csum_cmd |= TX_TCPCS_C;
> > +     else if (ip_protocol == IPPROTO_UDP)
> > +             csum_cmd |= TX_UDPCS_C;
> > +     else
> > +             WARN_ON_ONCE(1);
> > +
> > +     csum_cmd |= u32_encode_bits(skb_transport_offset(skb),
> > + TCPHO_MASK);
> > +
> > +     return csum_cmd;
> > +}
> > +
> > +static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
> > +                         u32 opts1, u32 opts2) {
> > +     const struct skb_shared_info *info = skb_shinfo(skb);
> > +     const struct rtase_private *tp = ring->ivec->tp;
> > +     const u8 nr_frags = info->nr_frags;
> > +     struct tx_desc *txd = NULL;
> > +     u32 cur_frag, entry;
> > +     u64 pkt_len_cnt = 0;
> 
> pkt_len_cnt is initialised here...
> 
> > +
> > +     entry = ring->cur_idx;
> > +     for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
> > +             const skb_frag_t *frag = &info->frags[cur_frag];
> > +             dma_addr_t mapping;
> > +             u32 status, len;
> > +             void *addr;
> > +
> > +             entry = (entry + 1) % NUM_DESC;
> > +
> > +             txd = ring->desc + sizeof(struct tx_desc) * entry;
> > +             len = skb_frag_size(frag);
> > +             addr = skb_frag_address(frag);
> > +             mapping = dma_map_single(&tp->pdev->dev, addr, len,
> > +                                      DMA_TO_DEVICE);
> > +
> > +             if (unlikely(dma_mapping_error(&tp->pdev->dev,
> mapping))) {
> > +                     if (unlikely(net_ratelimit()))
> > +                             netdev_err(tp->dev,
> > +                                        "Failed to map TX
> fragments
> > + DMA!\n");
> > +
> > +                     goto err_out;
> > +             }
> > +
> > +             if (((entry + 1) % NUM_DESC) == 0)
> > +                     status = (opts1 | len | RING_END);
> > +             else
> > +                     status = opts1 | len;
> > +
> > +             if (cur_frag == (nr_frags - 1)) {
> > +                     ring->skbuff[entry] = skb;
> > +                     status |= TX_LAST_FRAG;
> > +             }
> > +
> > +             ring->mis.len[entry] = len;
> > +             txd->addr = cpu_to_le64(mapping);
> > +             txd->opts2 = cpu_to_le32(opts2);
> > +
> > +             /* make sure the operating fields have been updated */
> > +             wmb();
> > +             txd->opts1 = cpu_to_le32(status);
> > +             pkt_len_cnt += len;
> 
> ... and incremented here. But is otherwise unused.
> 
> As flagged by a clang-16 W=1 build.

Thanks, I will remove this redundant code to fix this compiler warning
> 
> > +     }
> > +
> > +     return cur_frag;
> > +
> > +err_out:
> > +     rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
> > +     return -EIO;
> > +}
> 
> ...
> 
> > +static void rtase_dump_state(const struct net_device *dev) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters;
> > +     int max_reg_size = RTASE_PCI_REGS_SIZE;
> > +     const struct rtase_ring *ring;
> > +     u32 dword_rd;
> > +     int n = 0;
> > +
> > +     ring = &tp->tx_ring[0];
> > +     netdev_err(dev, "Tx descriptor info:\n");
> > +     netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx);
> > +     netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx);
> > +     netdev_err(dev, "Tx phyAddr = 0x%llx\n", ring->phy_addr);
> > +
> > +     ring = &tp->rx_ring[0];
> > +     netdev_err(dev, "Rx descriptor info:\n");
> > +     netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx);
> > +     netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx);
> > +     netdev_err(dev, "Rx phyAddr = 0x%llx\n", ring->phy_addr);
> > +
> > +     netdev_err(dev, "Device Registers:\n");
> > +     netdev_err(dev, "Chip Command = 0x%02x\n", rtase_r8(tp,
> RTASE_CHIP_CMD));
> > +     netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0));
> > +     netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0));
> > +     netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n",
> > +                rtase_r16(tp, RTASE_BOOT_CTL));
> > +     netdev_err(dev, "EPHY ISR(0xE014) = %04x\n",
> > +                rtase_r16(tp, RTASE_EPHY_ISR));
> > +     netdev_err(dev, "EPHY IMR(0xE016) = %04x\n",
> > +                rtase_r16(tp, RTASE_EPHY_IMR));
> > +     netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n",
> > +                rtase_r16(tp, RTASE_CLKSW_SET));
> > +
> > +     netdev_err(dev, "Dump PCI Registers:\n");
> > +
> > +     while (n < max_reg_size) {
> > +             if ((n % RTASE_DWORD_MOD) == 0)
> > +                     netdev_err(tp->dev, "0x%03x:\n", n);
> > +
> > +             pci_read_config_dword(tp->pdev, n, &dword_rd);
> > +             netdev_err(tp->dev, "%08x\n", dword_rd);
> > +             n += 4;
> > +     }
> > +
> > +     netdev_err(dev, "Dump tally counter:\n");
> > +     counters = tp->tally_vaddr;
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     netdev_err(dev, "tx_packets %lld\n",
> > +                le64_to_cpu(counters->tx_packets));
> 
> tx_packets is __le16 not __le64, so I think you want:
> 
>         netdev_err(dev, "rx_missed %d\n",
>                    le16_to_cpu(counters->rx_missed));
> 
> Likewise for align_errors, tx_aborted, and tx_underun.
> 
> As flagged by Sparse
> 
> > +     netdev_err(dev, "rx_packets %lld\n",
> > +                le64_to_cpu(counters->rx_packets));
> > +     netdev_err(dev, "tx_errors %lld\n",
> > +                le64_to_cpu(counters->tx_errors));
> > +     netdev_err(dev, "rx_missed %lld\n",
> > +                le64_to_cpu(counters->rx_missed));
> > +     netdev_err(dev, "align_errors %lld\n",
> > +                le64_to_cpu(counters->align_errors));
> > +     netdev_err(dev, "tx_one_collision %lld\n",
> > +                le64_to_cpu(counters->tx_one_collision));
> 
> Similarly, tx_one_collision is __le32 not __le64, so I think you want:
> .
>         netdev_err(dev, "tx_one_collision %d\n",
>                    le32_to_cpu(counters->tx_one_collision));
> 
> Likewise for tx_multi_collision, and rx_multicast.
> 
> There area also similar problems in rtase_main.c:rtase_dump_state(), added
> elsewhere in this patch-set.

Thank you for your review, I will correct these little endian issues.
> 
> > +     netdev_err(dev, "tx_multi_collision %lld\n",
> > +                le64_to_cpu(counters->tx_multi_collision));
> > +     netdev_err(dev, "rx_unicast %lld\n",
> > +                le64_to_cpu(counters->rx_unicast));
> > +     netdev_err(dev, "rx_broadcast %lld\n",
> > +                le64_to_cpu(counters->rx_broadcast));
> > +     netdev_err(dev, "rx_multicast %lld\n",
> > +                le64_to_cpu(counters->rx_multicast));
> > +     netdev_err(dev, "tx_aborted %lld\n",
> > +                le64_to_cpu(counters->tx_aborted));
> > +     netdev_err(dev, "tx_underun %lld\n",
> > +                le64_to_cpu(counters->tx_underun));
> > +}
> 
> ...
> 
> > +static const char rtase_gstrings[][ETH_GSTRING_LEN] = {
> > +     "tx_packets",
> > +     "rx_packets",
> > +     "tx_errors",
> > +     "rx_errors",
> > +     "rx_missed",
> > +     "align_errors",
> > +     "tx_single_collisions",
> > +     "tx_multi_collisions",
> > +     "unicast",
> > +     "broadcast",
> > +     "multicast",
> > +     "tx_aborted",
> > +     "tx_underrun",
> > +};
> > +
> > +static void rtase_get_strings(struct net_device *dev, u32 stringset,
> > +u8 *data) {
> > +     switch (stringset) {
> > +     case ETH_SS_STATS:
> > +             memcpy(data, *rtase_gstrings, sizeof(rtase_gstrings));
> 
> Compilers don't think this is seem correct, because *rtase_gsrings is the first
> element of that array, rather than the entire array. And thus is only
> ETH_GSTRING_LEN bytes long, rather than n * ETH_GSTRING_LEN bytes long.
> 
> I think what you want is (compile tested only!):
> 
>                 memcpy(data, rtase_gstrings, sizeof(rtase_gstrings));
> 
> Flagged by a clang-16 W=1 build.

Thanks for your suggestion, I will fix it.
> 
> > +             break;
> > +     }
> > +}
> 
> ...
> 
> > +static int rtase_init_board(struct pci_dev *pdev, struct net_device
> **dev_out,
> > +                         void __iomem **ioaddr_out) {
> > +     struct net_device *dev;
> > +     void __iomem *ioaddr;
> > +     int ret = -ENOMEM;
> > +
> > +     /* dev zeroed in alloc_etherdev */
> > +     dev = alloc_etherdev_mq(sizeof(struct rtase_private),
> > +                             RTASE_FUNC_TXQ_NUM);
> > +     if (!dev)
> > +             goto err_out;
> > +
> > +     SET_NETDEV_DEV(dev, &pdev->dev);
> > +
> > +     ret = pci_enable_device(pdev);
> > +     if (ret < 0)
> > +             goto err_out_free_dev;
> > +
> > +     /* make sure PCI base addr 1 is MMIO */
> > +     if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> > +             ret = -ENODEV;
> > +             goto err_out_disable;
> > +     }
> > +
> > +     /* check for weird/broken PCI region reporting */
> > +     if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) {
> > +             ret = -ENODEV;
> > +             goto err_out_disable;
> > +     }
> > +
> > +     ret = pci_request_regions(pdev, KBUILD_MODNAME);
> > +     if (ret < 0)
> > +             goto err_out_disable;
> > +
> > +     if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
> > +             dev->features |= NETIF_F_HIGHDMA;
> > +     else if (dma_set_mask_and_coherent(&pdev->dev,
> DMA_BIT_MASK(32)))
> > +             goto err_out_free_res;
> > +     else
> > +             dev_info(&pdev->dev, "DMA_BIT_MASK: 32\n");
> > +
> > +     pci_set_master(pdev);
> > +
> > +     /* ioremap MMIO region */
> > +     ioaddr = ioremap(pci_resource_start(pdev, 2),
> > +                      pci_resource_len(pdev, 2));
> > +     if (!ioaddr) {
> > +             ret = -EIO;
> > +             goto err_out_free_res;
> > +     }
> > +
> > +     *ioaddr_out = ioaddr;
> > +     *dev_out = dev;
> > +     goto out;
> 
> nit: I think it's slightly more idiomatic to simply return 0 here,
>      and drop the out label below.

Thanks, I will modify it.
> 
> > +
> > +err_out_free_res:
> > +     pci_release_regions(pdev);
> > +
> > +err_out_disable:
> > +     pci_disable_device(pdev);
> > +
> > +err_out_free_dev:
> > +     free_netdev(dev);
> > +
> > +err_out:
> > +     *ioaddr_out = NULL;
> > +     *dev_out = NULL;
> > +
> > +out:
> > +     return ret;
> > +}
> 
> ...
> 
> > diff --git a/drivers/net/ethernet/realtek/rtase/tt.h
> > b/drivers/net/ethernet/realtek/rtase/tt.h
> > new file mode 100644
> > index 000000000000..9239c518c504
> > --- /dev/null
> > +++ b/drivers/net/ethernet/realtek/rtase/tt.h
> > @@ -0,0 +1,353 @@
> > +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> > +/*
> > + *  rtase is the Linux device driver released for Realtek Automotive
> > +Switch
> > + *  controllers with PCI-Express interface.
> > + *
> > + *  Copyright(c) 2023 Realtek Semiconductor Corp.
> > + */
> > +
> > +#ifndef _RTASE_H_
> > +#define _RTASE_H_
> 
> If the code in tt.h is only used in tt.c, then perhaps the code can simply be
> moved into tt.c.

Sorry, tt.c and tt.h are redundant codes, I will remove them.
> 
> ...

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2023-11-23  3:49 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-15 13:34 [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 01/13] net:ethernet:realtek:rtase: Add pci table supported in this module Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 02/13] net:ethernet:realtek:rtase: Implement the .ndo_open function Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 03/13] net:ethernet:realtek:rtase: Implement the rtase_down function Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 04/13] net:ethernet:realtek:rtase: Implement the interrupt routine and rtase_poll Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 05/13] net:ethernet:realtek:rtase: Implement hardware configuration function Justin Lai
2023-11-15 15:02   ` Heiner Kallweit
2023-11-21 11:54     ` Justin Lai
2023-11-16 18:06   ` Simon Horman
2023-11-23  3:48     ` Justin Lai
2023-11-18 22:50   ` Jakub Kicinski
2023-11-20 13:22     ` Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 06/13] net:ethernet:realtek:rtase: Implement .ndo_start_xmit function Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 07/13] net:ethernet:realtek:rtase: Implement a function to receive packets Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 08/13] net:ethernet:realtek:rtase: Implement net_device_ops Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function Justin Lai
2023-11-15 15:02   ` Heiner Kallweit
2023-11-21  9:30     ` Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 10/13] net:ethernet:realtek:rtase: Implement ethtool function Justin Lai
2023-11-15 15:02   ` Heiner Kallweit
2023-11-23  3:16     ` Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 11/13] net:ethernet:realtek:rtase: Add a Makefile in the rtase folder Justin Lai
2023-11-15 13:34 ` [PATCH net-next v11 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder Justin Lai
2023-11-15 19:58   ` kernel test robot
2023-11-21 10:00   ` kernel test robot
2023-11-15 13:34 ` [PATCH net-next v11 13/13] MAINTAINERS: Add the rtase ethernet driver entry Justin Lai
2023-11-16 17:57 ` [PATCH net-next v11 00/13] Add Realtek automotive PCIe driver Simon Horman
2023-11-21  9:35   ` Justin Lai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).