rtase: Add support for a pci table in this module
authorJustin Lai <justinlai0215@realtek.com>
Wed, 4 Sep 2024 03:21:02 +0000 (11:21 +0800)
committerJakub Kicinski <kuba@kernel.org>
Fri, 6 Sep 2024 05:02:37 +0000 (22:02 -0700)
Add support for a pci table 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>
Link: https://patch.msgid.link/20240904032114.247117-2-justinlai0215@realtek.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/realtek/rtase/rtase.h [new file with mode: 0644]
drivers/net/ethernet/realtek/rtase/rtase_main.c [new file with mode: 0644]

diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h
new file mode 100644 (file)
index 0000000..583c339
--- /dev/null
@@ -0,0 +1,340 @@
+/* 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) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef RTASE_H
+#define RTASE_H
+
+#define RTASE_HW_VER_MASK 0x7C800000
+
+#define RTASE_RX_DMA_BURST_256       4
+#define RTASE_TX_DMA_BURST_UNLIMITED 7
+
+#define RTASE_RX_BUF_SIZE    (PAGE_SIZE - \
+                             SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+#define RTASE_MAX_JUMBO_SIZE (RTASE_RX_BUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN)
+
+/* 3 means InterFrameGap = the shortest one */
+#define RTASE_INTERFRAMEGAP 0x03
+
+#define RTASE_REGS_SIZE     256
+#define RTASE_PCI_REGS_SIZE 0x100
+
+#define RTASE_MULTICAST_FILTER_MASK GENMASK(30, 26)
+
+#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 RTASE_MITI_TIME_COUNT_MASK    GENMASK(3, 0)
+#define RTASE_MITI_TIME_UNIT_MASK     GENMASK(7, 4)
+#define RTASE_MITI_DEFAULT_TIME       128
+#define RTASE_MITI_MAX_TIME           491520
+#define RTASE_MITI_PKT_NUM_COUNT_MASK GENMASK(11, 8)
+#define RTASE_MITI_PKT_NUM_UNIT_MASK  GENMASK(13, 12)
+#define RTASE_MITI_DEFAULT_PKT_NUM    64
+#define RTASE_MITI_MAX_PKT_NUM_IDX    3
+#define RTASE_MITI_MAX_PKT_NUM_UNIT   16
+#define RTASE_MITI_MAX_PKT_NUM        240
+#define RTASE_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 RTASE_COUNTER_RESET BIT(0)
+#define RTASE_COUNTER_DUMP  BIT(3)
+
+       RTASE_FCR = 0x0018,
+#define RTASE_FCR_RXQ_MASK GENMASK(5, 4)
+
+       RTASE_LBK_CTRL = 0x001A,
+#define RTASE_LBK_ATLD BIT(1)
+#define RTASE_LBK_CLR  BIT(0)
+
+       RTASE_TX_DESC_ADDR0   = 0x0020,
+       RTASE_TX_DESC_ADDR4   = 0x0024,
+       RTASE_TX_DESC_COMMAND = 0x0028,
+#define RTASE_TX_DESC_CMD_CS BIT(15)
+#define RTASE_TX_DESC_CMD_WE BIT(14)
+
+       RTASE_BOOT_CTL  = 0x6004,
+       RTASE_CLKSW_SET = 0x6018,
+
+       RTASE_CHIP_CMD = 0x0037,
+#define RTASE_STOP_REQ      BIT(7)
+#define RTASE_STOP_REQ_DONE BIT(6)
+#define RTASE_RE            BIT(3)
+#define RTASE_TE            BIT(2)
+
+       RTASE_IMR0 = 0x0038,
+       RTASE_ISR0 = 0x003C,
+#define RTASE_TOK7 BIT(30)
+#define RTASE_TOK6 BIT(28)
+#define RTASE_TOK5 BIT(26)
+#define RTASE_TOK4 BIT(24)
+#define RTASE_FOVW BIT(6)
+#define RTASE_RDU  BIT(4)
+#define RTASE_TOK  BIT(2)
+#define RTASE_ROK  BIT(0)
+
+       RTASE_IMR1 = 0x0800,
+       RTASE_ISR1 = 0x0802,
+#define RTASE_Q_TOK BIT(4)
+#define RTASE_Q_RDU BIT(1)
+#define RTASE_Q_ROK BIT(0)
+
+       RTASE_EPHY_ISR = 0x6014,
+       RTASE_EPHY_IMR = 0x6016,
+
+       RTASE_TX_CONFIG_0 = 0x0040,
+#define RTASE_TX_INTER_FRAME_GAP_MASK GENMASK(25, 24)
+       /* DMA burst value (0-7) is shift this many bits */
+#define RTASE_TX_DMA_MASK             GENMASK(10, 8)
+
+       RTASE_RX_CONFIG_0 = 0x0044,
+#define RTASE_RX_SINGLE_FETCH  BIT(14)
+#define RTASE_RX_SINGLE_TAG    BIT(13)
+#define RTASE_RX_MX_DMA_MASK   GENMASK(10, 8)
+#define RTASE_ACPT_FLOW        BIT(7)
+#define RTASE_ACCEPT_ERR       BIT(5)
+#define RTASE_ACCEPT_RUNT      BIT(4)
+#define RTASE_ACCEPT_BROADCAST BIT(3)
+#define RTASE_ACCEPT_MULTICAST BIT(2)
+#define RTASE_ACCEPT_MYPHYS    BIT(1)
+#define RTASE_ACCEPT_ALLPHYS   BIT(0)
+#define RTASE_ACCEPT_MASK      (RTASE_ACPT_FLOW | RTASE_ACCEPT_ERR | \
+                               RTASE_ACCEPT_RUNT | RTASE_ACCEPT_BROADCAST | \
+                               RTASE_ACCEPT_MULTICAST | RTASE_ACCEPT_MYPHYS | \
+                               RTASE_ACCEPT_ALLPHYS)
+
+       RTASE_RX_CONFIG_1 = 0x0046,
+#define RTASE_RX_MAX_FETCH_DESC_MASK GENMASK(15, 11)
+#define RTASE_RX_NEW_DESC_FORMAT_EN  BIT(8)
+#define RTASE_OUTER_VLAN_DETAG_EN    BIT(7)
+#define RTASE_INNER_VLAN_DETAG_EN    BIT(6)
+#define RTASE_PCIE_NEW_FLOW          BIT(2)
+#define RTASE_PCIE_RELOAD_EN         BIT(0)
+
+       RTASE_EEM = 0x0050,
+#define RTASE_EEM_UNLOCK 0xC0
+
+       RTASE_TDFNR  = 0x0057,
+       RTASE_TPPOLL = 0x0090,
+       RTASE_PDR    = 0x00B0,
+       RTASE_FIFOR  = 0x00D3,
+#define RTASE_TX_FIFO_EMPTY BIT(5)
+#define RTASE_RX_FIFO_EMPTY BIT(4)
+
+       RTASE_RMS       = 0x00DA,
+       RTASE_CPLUS_CMD = 0x00E0,
+#define RTASE_FORCE_RXFLOW_EN BIT(11)
+#define RTASE_FORCE_TXFLOW_EN BIT(10)
+#define RTASE_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 RTASE_TAG_NUM_SEL_MASK GENMASK(10, 8)
+
+       RTASE_MISC = 0x00F2,
+#define RTASE_RX_DV_GATE_EN BIT(3)
+
+       RTASE_TFUN_CTRL = 0x0400,
+#define RTASE_TX_NEW_DESC_FORMAT_EN BIT(0)
+
+       RTASE_TX_CONFIG_1 = 0x203E,
+#define RTASE_TC_MODE_MASK GENMASK(11, 10)
+
+       RTASE_TOKSEL      = 0x2046,
+       RTASE_RFIFONFULL  = 0x4406,
+       RTASE_INT_MITI_TX = 0x0A00,
+       RTASE_INT_MITI_RX = 0x0A80,
+
+       RTASE_VLAN_ENTRY_0     = 0xAC80,
+};
+
+enum rtase_desc_status_bit {
+       RTASE_DESC_OWN = BIT(31), /* Descriptor is owned by NIC */
+       RTASE_RING_END = BIT(30), /* End of descriptor ring */
+};
+
+enum rtase_sw_flag_content {
+       RTASE_SWF_MSI_ENABLED  = BIT(1),
+       RTASE_SWF_MSIX_ENABLED = BIT(2),
+};
+
+#define RSVD_MASK 0x3FFFC000
+
+struct rtase_tx_desc {
+       __le32 opts1;
+       __le32 opts2;
+       __le64 addr;
+       __le32 opts3;
+       __le32 reserved1;
+       __le32 reserved2;
+       __le32 reserved3;
+} __packed;
+
+/*------ offset 0 of tx descriptor ------*/
+#define RTASE_TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */
+#define RTASE_TX_LAST_FRAG  BIT(28) /* Tx Final segment of a packet */
+#define RTASE_GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */
+#define RTASE_GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */
+#define RTASE_TX_VLAN_TAG   BIT(17) /* Add VLAN tag */
+
+/*------ offset 4 of tx descriptor ------*/
+#define RTASE_TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */
+#define RTASE_TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */
+#define RTASE_TX_IPCS_C  BIT(29) /* Calculate IP checksum */
+#define RTASE_TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */
+
+union rtase_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 RTASE_RX_FIRST_FRAG BIT(25) /* Rx First segment of a packet */
+#define RTASE_RX_LAST_FRAG  BIT(24) /* Rx Final segment of a packet */
+#define RTASE_RX_RES        BIT(20)
+#define RTASE_RX_RUNT       BIT(19)
+#define RTASE_RX_RWT        BIT(18)
+#define RTASE_RX_CRC        BIT(16)
+#define RTASE_RX_V6F        BIT(31)
+#define RTASE_RX_V4F        BIT(30)
+#define RTASE_RX_UDPT       BIT(29)
+#define RTASE_RX_TCPT       BIT(28)
+#define RTASE_RX_IPF        BIT(26) /* IP checksum failed */
+#define RTASE_RX_UDPF       BIT(25) /* UDP/IP checksum failed */
+#define RTASE_RX_TCPF       BIT(24) /* TCP/IP checksum failed */
+#define RTASE_RX_VLAN_TAG   BIT(16) /* VLAN tag available */
+
+#define RTASE_NUM_DESC          1024
+#define RTASE_TX_BUDGET_DEFAULT 256
+#define RTASE_TX_RING_DESC_SIZE (RTASE_NUM_DESC * sizeof(struct rtase_tx_desc))
+#define RTASE_RX_RING_DESC_SIZE (RTASE_NUM_DESC * sizeof(union rtase_rx_desc))
+#define RTASE_TX_STOP_THRS      (MAX_SKB_FRAGS + 1)
+#define RTASE_TX_START_THRS     (2 * RTASE_TX_STOP_THRS)
+#define RTASE_VLAN_TAG_MASK     GENMASK(15, 0)
+#define RTASE_RX_PKT_SIZE_MASK  GENMASK(13, 0)
+
+#define RTASE_IVEC_NAME_SIZE (IFNAMSIZ + 10)
+
+struct rtase_int_vector {
+       struct rtase_private *tp;
+       unsigned int irq;
+       char name[RTASE_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[RTASE_NUM_DESC];
+       void *data_buf[RTASE_NUM_DESC];
+       union {
+               u32 len[RTASE_NUM_DESC];
+               dma_addr_t data_phy_addr[RTASE_NUM_DESC];
+       } mis;
+
+       struct list_head ring_entry;
+       int (*ring_handler)(struct rtase_ring *ring, int budget);
+       u64 alloc_fail;
+};
+
+struct rtase_stats {
+       u64 tx_dropped;
+       u64 rx_dropped;
+       u64 multicast;
+       u64 rx_errors;
+       u64 rx_length_errors;
+       u64 rx_crc_errors;
+};
+
+struct rtase_private {
+       void __iomem *mmio_addr;
+       u32 sw_flag;
+
+       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_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 msix_entry msix_entry[RTASE_NUM_MSIX];
+       struct rtase_int_vector int_vector[RTASE_NUM_MSIX];
+
+       struct rtase_stats stats;
+
+       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 RTASE_LSO_64K 64000
+
+#define RTASE_NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4)
+
+#define RTASE_TCPHO_MASK GENMASK(24, 18)
+
+#define RTASE_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 (file)
index 0000000..2aa472d
--- /dev/null
@@ -0,0 +1,638 @@
+// 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) 2024 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. Linux only plays
+ *  the role of a normal leaf node in this model.
+ */
+
+#include <linux/crc32.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/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/prefetch.h>
+#include <linux/rtnetlink.h>
+#include <linux/tcp.h>
+#include <asm/irq.h>
+#include <net/ip6_checksum.h>
+#include <net/netdev_queues.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 | RTASE_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 | RTASE_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 & ~RTASE_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, RTASE_LBK_ATLD | RTASE_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_hw_addr_random(dev);
+               netdev_warn(dev, "Random ether addr %pM\n", dev->dev_addr);
+       } else {
+               eth_hw_addr_set(dev, mac_addr);
+               ether_addr_copy(dev->perm_addr, dev->dev_addr);
+       }
+
+       rtase_rar_set(tp, dev->dev_addr);
+}
+
+static void rtase_reset_interrupt(struct pci_dev *pdev,
+                                 const struct rtase_private *tp)
+{
+       if (tp->sw_flag & RTASE_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, irq;
+       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_exact(pdev, tp->msix_entry, tp->int_nums);
+
+       if (ret)
+               return ret;
+
+       for (i = 0; i < tp->int_nums; i++) {
+               irq = pci_irq_vector(pdev, i);
+               if (!irq) {
+                       pci_disable_msix(pdev);
+                       return irq;
+               }
+
+               tp->int_vector[i].irq = irq;
+       }
+
+       return 0;
+}
+
+static int rtase_alloc_interrupt(struct pci_dev *pdev,
+                                struct rtase_private *tp)
+{
+       int ret;
+
+       ret = rtase_alloc_msix(pdev, tp);
+       if (ret) {
+               ret = pci_enable_msi(pdev);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "unable to alloc interrupt.(MSI)\n");
+                       return ret;
+               }
+
+               tp->sw_flag |= RTASE_SWF_MSI_ENABLED;
+       } else {
+               tp->sw_flag |= RTASE_SWF_MSIX_ENABLED;
+       }
+
+       return 0;
+}
+
+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 = RTASE_ROK | RTASE_RDU | RTASE_TOK |
+                               RTASE_TOK4 | RTASE_TOK5 | RTASE_TOK6 |
+                               RTASE_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);
+
+       /* 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 = RTASE_Q_ROK | RTASE_Q_RDU |
+                                       RTASE_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);
+       }
+}
+
+static u16 rtase_calc_time_mitigation(u32 time_us)
+{
+       u8 msb, time_count, time_unit;
+       u16 int_miti;
+
+       time_us = min_t(int, time_us, RTASE_MITI_MAX_TIME);
+
+       msb = fls(time_us);
+       if (msb >= RTASE_MITI_COUNT_BIT_NUM) {
+               time_unit = msb - RTASE_MITI_COUNT_BIT_NUM;
+               time_count = time_us >> (msb - RTASE_MITI_COUNT_BIT_NUM);
+       } else {
+               time_unit = 0;
+               time_count = time_us;
+       }
+
+       int_miti = u16_encode_bits(time_count, RTASE_MITI_TIME_COUNT_MASK) |
+                  u16_encode_bits(time_unit, RTASE_MITI_TIME_UNIT_MASK);
+
+       return int_miti;
+}
+
+static u16 rtase_calc_packet_num_mitigation(u16 pkt_num)
+{
+       u8 msb, pkt_num_count, pkt_num_unit;
+       u16 int_miti;
+
+       pkt_num = min_t(int, pkt_num, RTASE_MITI_MAX_PKT_NUM);
+
+       if (pkt_num > 60) {
+               pkt_num_unit = RTASE_MITI_MAX_PKT_NUM_IDX;
+               pkt_num_count = pkt_num / RTASE_MITI_MAX_PKT_NUM_UNIT;
+       } else {
+               msb = fls(pkt_num);
+               if (msb >= RTASE_MITI_COUNT_BIT_NUM) {
+                       pkt_num_unit = msb - RTASE_MITI_COUNT_BIT_NUM;
+                       pkt_num_count = pkt_num >> (msb -
+                                                   RTASE_MITI_COUNT_BIT_NUM);
+               } else {
+                       pkt_num_unit = 0;
+                       pkt_num_count = pkt_num;
+               }
+       }
+
+       int_miti = u16_encode_bits(pkt_num_count,
+                                  RTASE_MITI_PKT_NUM_COUNT_MASK) |
+                  u16_encode_bits(pkt_num_unit,
+                                  RTASE_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(RTASE_MITI_DEFAULT_TIME) |
+                  rtase_calc_packet_num_mitigation(RTASE_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 = RTASE_MAX_JUMBO_SIZE;
+}
+
+static bool rtase_check_mac_version_valid(struct rtase_private *tp)
+{
+       u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & RTASE_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_err(&pdev->dev, "no usable dma addressing method\n");
+               goto err_out_free_res;
+       }
+
+       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;
+
+       return ret;
+
+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;
+
+       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->dev->perm_addr);
+       iounmap(ioaddr);
+
+       if (tp->sw_flag & RTASE_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;
+       struct rtase_int_vector *ivec;
+       void __iomem *ioaddr = NULL;
+       struct rtase_private *tp;
+       int ret, i;
+
+       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");
+
+       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->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
+
+       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+                        NETIF_F_IP_CSUM | NETIF_F_HIGHDMA |
+                        NETIF_F_RXCSUM | NETIF_F_SG |
+                        NETIF_F_TSO | 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 |
+                          NETIF_F_RXALL | NETIF_F_RXFCS |
+                          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, RTASE_LSO_64K);
+       netif_set_tso_max_segs(dev, RTASE_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);
+
+       netif_carrier_off(dev);
+
+       ret = register_netdev(dev);
+       if (ret != 0)
+               goto err_out;
+
+       netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq);
+
+       return 0;
+
+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:
+       for (i = 0; i < tp->int_nums; i++) {
+               ivec = &tp->int_vector[i];
+               netif_napi_del(&ivec->napi);
+       }
+
+       rtase_release_board(pdev, dev, ioaddr);
+
+       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;
+
+       unregister_netdev(dev);
+
+       for (i = 0; i < tp->int_nums; i++) {
+               ivec = &tp->int_vector[i];
+               netif_napi_del(&ivec->napi);
+       }
+
+       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;
+
+       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);