From 5deaf74c4b3edcf88f67f18aa352690deb9dc212 Mon Sep 17 00:00:00 2001 From: "gu.huiguang" Date: Thu, 7 Mar 2024 19:16:01 +0800 Subject: [PATCH] drivers: initial support for rnpm drivers from Mucse Technology mucse inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I94HH5 CVE: NA ---------------------------------------- Mucse RNPM drivers provide Ethernet features for mucse N10/N400 series NICs. Signed-off-by: guhuiguang --- arch/arm64/configs/openeuler_defconfig | 3 + arch/x86/configs/openeuler_defconfig | 3 + drivers/net/ethernet/mucse/Kconfig | 30 +- drivers/net/ethernet/mucse/Makefile | 1 + drivers/net/ethernet/mucse/rnpm/Makefile | 18 + drivers/net/ethernet/mucse/rnpm/rnpm.h | 1236 +++ drivers/net/ethernet/mucse/rnpm/rnpm_common.c | 2318 +++++ drivers/net/ethernet/mucse/rnpm/rnpm_common.h | 372 + .../net/ethernet/mucse/rnpm/rnpm_debugfs.c | 389 + .../net/ethernet/mucse/rnpm/rnpm_ethtool.c | 3335 ++++++ drivers/net/ethernet/mucse/rnpm/rnpm_lib.c | 1497 +++ drivers/net/ethernet/mucse/rnpm/rnpm_main.c | 9250 +++++++++++++++++ drivers/net/ethernet/mucse/rnpm/rnpm_mbx.c | 669 ++ drivers/net/ethernet/mucse/rnpm/rnpm_mbx.h | 156 + drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.c | 1521 +++ drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.h | 1201 +++ drivers/net/ethernet/mucse/rnpm/rnpm_mpe.c | 219 + drivers/net/ethernet/mucse/rnpm/rnpm_mpe.h | 14 + drivers/net/ethernet/mucse/rnpm/rnpm_n10.c | 738 ++ drivers/net/ethernet/mucse/rnpm/rnpm_pcs.c | 35 + drivers/net/ethernet/mucse/rnpm/rnpm_pcs.h | 9 + drivers/net/ethernet/mucse/rnpm/rnpm_phy.c | 674 ++ drivers/net/ethernet/mucse/rnpm/rnpm_phy.h | 115 + drivers/net/ethernet/mucse/rnpm/rnpm_ptp.c | 804 ++ drivers/net/ethernet/mucse/rnpm/rnpm_ptp.h | 93 + drivers/net/ethernet/mucse/rnpm/rnpm_regs.h | 667 ++ drivers/net/ethernet/mucse/rnpm/rnpm_sriov.c | 1088 ++ drivers/net/ethernet/mucse/rnpm/rnpm_sriov.h | 37 + drivers/net/ethernet/mucse/rnpm/rnpm_sysfs.c | 1842 ++++ drivers/net/ethernet/mucse/rnpm/rnpm_tc.c | 34 + drivers/net/ethernet/mucse/rnpm/rnpm_tc.h | 27 + .../ethernet/mucse/rnpm/rnpm_tc_u32_parse.h | 54 + drivers/net/ethernet/mucse/rnpm/rnpm_type.h | 1072 ++ drivers/net/ethernet/mucse/rnpm/version.h | 7 + 34 files changed, 29527 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mucse/rnpm/Makefile create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_common.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_common.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_debugfs.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_ethtool.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_lib.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_main.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mbx.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mbx.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mpe.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_mpe.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_n10.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_pcs.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_pcs.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_phy.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_phy.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_ptp.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_ptp.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_regs.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_sriov.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_sriov.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_sysfs.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_tc.c create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_tc.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_tc_u32_parse.h create mode 100644 drivers/net/ethernet/mucse/rnpm/rnpm_type.h create mode 100644 drivers/net/ethernet/mucse/rnpm/version.h diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index f63c7ae1da40..22cb0b97f075 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -2924,6 +2924,9 @@ CONFIG_MXGBE_FIX_MAC_PADDING=y # CONFIG_MXGBE_OPTM_WITH_LARGE is not set CONFIG_MXGBE_MSIX_COUNT=64 CONFIG_MXGBE_DCB=y +CONFIG_MXGBEM=m +CONFIG_MXGBEM_FIX_MAC_PADDING=y +# CONFIG_MXGBEM_OPTM_WITH_LARGE is not set CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_EN=m CONFIG_MLX4_EN_DCB=y diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 6a581e054e8e..b022a37a4836 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -2927,6 +2927,9 @@ CONFIG_MXGBE_FIX_VF_QUEUE=y CONFIG_MXGBE_FIX_MAC_PADDING=y # CONFIG_MXGBE_OPTM_WITH_LARGE is not set CONFIG_MXGBE_MSIX_COUNT=64 +CONFIG_MXGBEM=m +CONFIG_MXGBEM_FIX_MAC_PADDING=y +# CONFIG_MXGBEM_OPTM_WITH_LARGE is not set CONFIG_MXGBE_DCB=y CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_EN=m diff --git a/drivers/net/ethernet/mucse/Kconfig b/drivers/net/ethernet/mucse/Kconfig index 85e5c6b574a4..2723446222cc 100644 --- a/drivers/net/ethernet/mucse/Kconfig +++ b/drivers/net/ethernet/mucse/Kconfig @@ -55,7 +55,6 @@ config MXGBE_OPTM_WITH_LARGE If unsure, say N. - config MXGBE_MSIX_COUNT int "Number of msix count" default "64" @@ -73,4 +72,33 @@ config MXGBE_DCB If unsure, say N. +config MXGBEM + tristate "Mucse(R) 1/10GbE PCI Express 4/8 ports adapters support" + depends on PCI + imply PTP_1588_CLOCK + help + This driver supports Mucse(R) 1/10GbE 4/8 ports PCI Express family of + adapters. + + To compile this driver as a module, choose M here. The module + will be called rnpm. + +config MXGBEM_FIX_MAC_PADDING + bool "Close Mac Padding Function(pf)" + default y + depends on MXGBEM + help + Say Y here if you want to fix close mac padding in the driver. + + If unsure, say N. + +config MXGBEM_OPTM_WITH_LARGE + bool "Reduce Memory Cost In Large PAGE_SIZE(>8192)" + default n + depends on MXGBEM + help + Say Y here if you want to reduce memory cost in large PAGE_SIZE. + + If unsure, say N. + endif # NET_VENDOR_MUCSE diff --git a/drivers/net/ethernet/mucse/Makefile b/drivers/net/ethernet/mucse/Makefile index 56ac992d24a9..ea3693e0022f 100644 --- a/drivers/net/ethernet/mucse/Makefile +++ b/drivers/net/ethernet/mucse/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_MXGBE) += rnp/ +obj-$(CONFIG_MXGBEM) += rnpm/ diff --git a/drivers/net/ethernet/mucse/rnpm/Makefile b/drivers/net/ethernet/mucse/rnpm/Makefile new file mode 100644 index 000000000000..9f60afc7cdb6 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/Makefile @@ -0,0 +1,18 @@ +obj-$(CONFIG_MXGBEM) += rnpm.o +rnpm-objs := \ + rnpm_common.o \ + rnpm_debugfs.o \ + rnpm_ethtool.o \ + rnpm_lib.o \ + rnpm_main.o \ + rnpm_mbx.o \ + rnpm_n10.o \ + rnpm_pcs.o \ + rnpm_mbx_fw.o\ + rnpm_phy.o \ + rnpm_sriov.o \ + rnpm_sysfs.o \ + rnpm_tc.o \ + rnpm_mpe.o \ + rnpm_ptp.o + diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm.h b/drivers/net/ethernet/mucse/rnpm/rnpm.h new file mode 100644 index 000000000000..99f1aa52d91b --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm.h @@ -0,0 +1,1236 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_H_ +#define _RNPM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rnpm_type.h" +#include "rnpm_common.h" + +/* common prefix used by pr_<> macros */ +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define RNPM_ALLOC_PAGE_ORDER (0) +#define RNPM_PAGE_BUFFER_NUMS(ring) \ + ((1 << RNPM_ALLOC_PAGE_ORDER) * PAGE_SIZE / \ + ALIGN((rnpm_rx_offset(ring) + rnpm_rx_bufsz(ring) + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ + RNPM_RX_HWTS_OFFSET), \ + 1024)) + +/* TX/RX descriptor defines */ +#ifdef FEITENG +#define RNPM_DEFAULT_TXD (1024) +#else +#define RNPM_DEFAULT_TXD (1024) +#endif + +#define RNPM_N400_DEFAULT_TXD (256) + +#define RNPM_DEFAULT_TX_WORK (256) +#define RNPM_MIN_TX_WORK (32) +#define RNPM_MAX_TX_WORK (512) +#define RNPM_MIN_RX_WORK (32) +#define RNPM_MAX_RX_WORK (512) +#define RNPM_WORK_ALIGN (2) +#define RNPM_MIN_TX_FRAME (1) +#define RNPM_MAX_TX_FRAME (256) +#define RNPM_MIN_TX_USEC (30) +#define RNPM_MAX_TX_USEC (10000) + +#define RNPM_DEFAULT_HIGH_RX_USEC (1600) +#define RNPM_DEFAULT_LOW_RX_USEC (200) + +#ifndef RNPM_IRQ_CHECK_USEC +#define RNPM_IRQ_CHECK_USEC 1000 +#endif + +#define RNPM_MIN_RX_FRAME (1) +#define RNPM_MAX_RX_FRAME (256) +#define RNPM_MIN_RX_USEC (10) +#define RNPM_MAX_RX_USEC (10000) + +#define RNP_MAX_VF_FUNCTIONS 64 + +#define RNPM_MAX_TXD (4096) +#define RNPM_MIN_TXD (64) + +#define RNPM_DEFAULT_SAMPLE_INTERVAL (10) +#define RNPM_DEFAULT_ENABLE (1) +#define RNPM_DEFAULT_DISABLE (0) +#define RNPM_DEFAULT_NAPI_BUDGE (64) +#define RNPM_REQ_TX_DESCRIPTOR_MULTIPLE (8) +#define RNPM_REQ_RX_DESCRIPTOR_MULTIPLE (8) + +#ifdef FEITENG +#define RNPM_DEFAULT_RXD (1024) +#else +#define RNPM_DEFAULT_RXD (1024) +#endif + +#define RNPM_MAX_RXD (4096) +#define RNPM_MIN_RXD (64) + +/* Phy */ +#define AUTO_ALL_MODES 0 + +/* flow control */ +#define RNPM_MIN_FCRTL (0x40) +#define RNPM_MAX_FCRTL (0x7FF80) +#define RNPM_MIN_FCRTH (0x600) +#define RNPM_MAX_FCRTH (0x7FFF0) +#define RNPM_DEFAULT_FCPAUSE (0xffff) +#define RNPM_DEFAULT_HIGH_WATER (0x320) +#define RNPM_DEFAULT_LOW_WATER (0x270) +#define RNPM_MIN_FCPAUSE (0) +#define RNPM_MAX_FCPAUSE (0xFFFF) + +/* Supported Rx Buffer Sizes */ +/* Used for skb receive header */ +#define RNPM_RXBUFFER_256 256 +#define RNPM_RXBUFFER_1536 1536 +#define RNPM_RXBUFFER_2K 2048 +#define RNPM_RXBUFFER_3K 3072 +#define RNPM_RXBUFFER_4K 4096 +#define RNPM_MAX_RXBUFFER 16384 /* largest size for a single descriptor */ +#define RNPM_RXBUFFER_MAX (RNPM_RXBUFFER_2K) + +#define MAX_Q_VECTORS 128 +#define RNPM_RING_COUNTS_PEER_PF 8 + +#ifdef NETIF_F_GSO_PARTIAL +#define RNPM_GSO_PARTIAL_FEATURES \ + (NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) +#endif /* NETIF_F_GSO_PARTIAL */ +/* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN means we + * reserve 64 more, and skb_shared_info adds an additional 320 bytes more, + * this adds up to 448 bytes of extra data. + * + * Since netdev_alloc_skb now allocates a page fragment we can use a value + * of 256 and the resultant skb will have a truesize of 960 or less. + */ +#define RNPM_RX_HDR_SIZE RNPM_RXBUFFER_256 + +#define RNPM_ITR_ADAPTIVE_MIN_INC 2 +#define RNPM_ITR_ADAPTIVE_MIN_USECS 8 +#define RNPM_ITR_ADAPTIVE_MAX_USECS 800 +#define RNPM_ITR_ADAPTIVE_LATENCY 0x400 +#define RNPM_ITR_ADAPTIVE_BULK 0x00 + +/* How many Rx Buffers do we bundle into one write to the hardware ? */ +#ifdef RNPM_OPTM_WITH_LPAGE +#define RNPM_RX_BUFFER_WRITE (PAGE_SIZE / 2048) /* Must be power of 2 */ +#else +#define RNPM_RX_BUFFER_WRITE 16 /* Must be power of 2 */ +#endif +enum rnpm_tx_flags { + /* cmd_type flags */ + RNPM_TX_FLAGS_HW_VLAN = 0x01, + RNPM_TX_FLAGS_TSO = 0x02, + RNPM_TX_FLAGS_TSTAMP = 0x04, + + /* olinfo flags */ + RNPM_TX_FLAGS_CC = 0x08, + RNPM_TX_FLAGS_IPV4 = 0x10, + RNPM_TX_FLAGS_CSUM = 0x20, + + /* software defined flags */ + RNPM_TX_FLAGS_SW_VLAN = 0x40, + RNPM_TX_FLAGS_FCOE = 0x80, +}; + +/* modify this in asic version */ +#define RNPM_MAX_VF_CNT 64 + +#define RNPM_RX_RATE_HIGH 450000 +#define RNPM_RX_COAL_TIME_HIGH 128 +#define RNPM_RX_SIZE_THRESH 1024 +#define RNPM_RX_RATE_THRESH (1000000 / RNPM_RX_COAL_TIME_HIGH) +#define RNPM_SAMPLE_INTERVAL 0 +#define RNPM_AVG_PKT_SMALL 256 + +#define RNPM_MAX_VF_MC_ENTRIES 30 +#define RNPM_MAX_VF_FUNCTIONS RNPM_MAX_VF_CNT +#define RNPM_MAX_VFTA_ENTRIES 128 +#define MAX_EMULATION_MAC_ADDRS 16 +#define RNPM_MAX_PF_MACVLANS 15 +#define PF_RING_CNT_WHEN_IOV_ENABLED 2 +#define VMDQ_P(p) ((p) + adapter->ring_feature[RING_F_VMDQ].offset) + +struct vf_data_storage { + unsigned char vf_mac_addresses[ETH_ALEN]; + u16 vf_mc_hashes[RNPM_MAX_VF_MC_ENTRIES]; + u16 num_vf_mc_hashes; + u16 default_vf_vlan_id; + u16 vlans_enabled; + bool clear_to_send; + bool pf_set_mac; + u16 pf_vlan; /* When set, guest VLAN config not allowed. */ + u16 pf_qos; + u16 tx_rate; + u16 vlan_count; + u8 spoofchk_enabled; + unsigned int vf_api; +}; + +struct vf_macvlans { + struct list_head l; + int vf; + int rar_entry; + bool free; + bool is_macvlan; + u8 vf_macvlan[ETH_ALEN]; +}; + +/* now tx max 4k for one desc */ +#define RNPM_MAX_TXD_PWR 12 +#define RNPM_MAX_DATA_PER_TXD (1 << RNPM_MAX_TXD_PWR) + +/* Tx Descriptors needed, worst case */ +#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), RNPM_MAX_DATA_PER_TXD) +#define DESC_NEEDED (MAX_SKB_FRAGS + 4) + +/* wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffers + */ +struct rnpm_tx_buffer { + struct rnpm_tx_desc *next_to_watch; + unsigned long time_stamp; + struct sk_buff *skb; + unsigned int bytecount; + unsigned short gso_segs; +#ifdef RNPM_FIX_MAC_PADDING + bool gso_need_padding; +#endif + __be16 protocol; + DEFINE_DMA_UNMAP_ADDR(dma); + DEFINE_DMA_UNMAP_LEN(len); + union { + u32 tx_flags; + struct { + u16 vlan; + u16 cmd_flags; + }; + }; + __le32 mac_ip_len; + /* for control desc */ + union { + u32 mss_len_vf_num; + struct { + __le16 mss_len; + u8 vf_num; + u8 l4_hdr_len; + }; + }; + union { + u32 inner_vlan_tunnel_len; + struct { + u8 tunnel_hdr_len; + u8 inner_vlan_l; + u8 inner_vlan_h; + u8 resv; + }; + }; + u32 type_tucmd; + bool ctx_flag; +}; + +struct rnpm_rx_buffer { + struct sk_buff *skb; + dma_addr_t dma; + struct page *page; +#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) + __u32 page_offset; +#else + __u16 page_offset; +#endif + __u16 pagecnt_bias; +}; + +struct rnpm_queue_stats { + u64 packets; + u64 bytes; +}; + +struct rnpm_tx_queue_stats { + u64 restart_queue; + u64 tx_busy; + u64 tx_done_old; + u64 clean_desc; + u64 poll_count; + u64 irq_more_count; + u64 send_bytes; + u64 send_bytes_to_hw; + u64 todo_update; + u64 send_done_bytes; + u64 vlan_add; + u64 tx_irq_miss; + u64 tx_next_to_clean; + u64 tx_equal_count; +}; + +struct rnpm_rx_queue_stats { + u64 driver_drop_packets; + u64 rsc_count; + u64 rsc_flush; + u64 non_eop_descs; + u64 alloc_rx_page_failed; + u64 alloc_rx_buff_failed; + u64 csum_err; + u64 csum_good; + u64 poll_again_count; + u64 vlan_remove; + u64 alloc_rx_page; + u64 rx_irq_miss; + u64 rx_next_to_clean; + u64 rx_equal_count; + u64 rx_poll_packets; + u64 rx_poll_avg_packets; + u64 rx_poll_itr; + //u64 poll_count; +}; + +enum rnpm_ring_state_t { + __RNPM_RX_3K_BUFFER, + __RNPM_RX_BUILD_SKB_ENABLED, + __RNPM_TX_FDIR_INIT_DONE, + __RNPM_TX_XPS_INIT_DONE, + __RNPM_TX_DETECT_HANG, + __RNPM_HANG_CHECK_ARMED, + //__RNPM_RX_RSC_ENABLED, + __RNPM_RX_CSUM_UDP_ZERO_ERR, + __RNPM_RX_FCOE, +}; + +#define ring_uses_build_skb(ring) \ + test_bit(__RNPM_RX_BUILD_SKB_ENABLED, &(ring)->state) + +#define check_for_tx_hang(ring) test_bit(__RNPM_TX_DETECT_HANG, &(ring)->state) +#define set_check_for_tx_hang(ring) \ + set_bit(__RNPM_TX_DETECT_HANG, &(ring)->state) +#define clear_check_for_tx_hang(ring) \ + clear_bit(__RNPM_TX_DETECT_HANG, &(ring)->state) + +#define netdev_ring(ring) (ring->netdev) +struct rnpm_ring { + struct rnpm_ring *next; /* pointer to next ring in q_vector */ + struct rnpm_q_vector *q_vector; /* backpointer to host q_vector */ + struct net_device *netdev; /* netdev ring belongs to */ + struct device *dev; /* device for DMA mapping */ + void *desc; /* descriptor ring memory */ + union { + struct rnpm_tx_buffer *tx_buffer_info; + struct rnpm_rx_buffer *rx_buffer_info; + }; + unsigned long last_rx_timestamp; + unsigned long state; + u8 __iomem *tail; + u8 __iomem *dma_hw_addr; + u8 __iomem *dma_int_stat; + u8 __iomem *dma_int_mask; + u8 __iomem *dma_int_clr; + dma_addr_t dma; /* phys. address of descriptor ring */ + unsigned int size; /* length in bytes */ + u32 ring_flags; +#define RNPM_RING_FLAG_DELAY_SETUP_RX_LEN ((u32)(1 << 0)) +#define RNPM_RING_FLAG_CHANGE_RX_LEN ((u32)(1 << 1)) +#define RNPM_RING_FLAG_DO_RESET_RX_LEN ((u32)(1 << 2)) + u8 pfvfnum; + u8 gso_padto_bytes; + + u16 count; /* amount of descriptors */ + u16 temp_count; + u16 reset_count; + + u8 queue_index; /* queue_index needed for multiqueue queue management */ + u8 rnpm_queue_idx; /*the real ring,used by dma*/ + u16 next_to_use; //tail (not-dma-mapped) + u16 next_to_clean; //soft-saved-head + + u16 device_id; +#ifdef RNPM_OPTM_WITH_LPAGE + u16 rx_page_buf_nums; + u32 rx_per_buf_mem; + struct sk_buff *skb; +#endif + union { + u16 next_to_alloc; + struct { + u8 atr_sample_rate; + u8 atr_count; + }; + }; + + u8 dcb_tc; + struct rnpm_queue_stats stats; + struct u64_stats_sync syncp; + union { + struct rnpm_tx_queue_stats tx_stats; + struct rnpm_rx_queue_stats rx_stats; + }; +} ____cacheline_internodealigned_in_smp; + +#define RING2ADAPT(ring) netdev_priv((ring)->netdev) + +enum rnpm_ring_f_enum { + RING_F_NONE = 0, + RING_F_VMDQ, /* SR-IOV uses the same ring feature */ + RING_F_RSS, + RING_F_FDIR, + + RING_F_ARRAY_SIZE /* must be last in enum set */ +}; + +#define RNPM_MAX_RSS_INDICES 128 +#define RNPM_MAX_RSS_INDICES_UV3P 8 +#define RNPM_MAX_VMDQ_INDICES 64 +#define RNPM_MAX_FDIR_INDICES 63 /* based on q_vector limit */ +#define RNPM_MAX_FCOE_INDICES 8 +#define MAX_RX_QUEUES (128) +#define MAX_TX_QUEUES (128) +#define MAX_PORT_NUM (4) /* one pf has 4 ports max */ +struct rnpm_ring_feature { + u16 limit; /* upper limit on feature indices */ + u16 indices; /* current value of indices */ + u16 mask; /* Mask used for feature to ring mapping */ + u16 offset; /* offset to start of feature */ +} ____cacheline_internodealigned_in_smp; + +#define RNPM_n10_VMDQ_8Q_MASK 0x78 +#define RNPM_n10_VMDQ_4Q_MASK 0x7C +#define RNPM_n10_VMDQ_2Q_MASK 0x7E + +/* FCoE requires that all Rx buffers be over 2200 bytes in length. Since + * this is twice the size of a half page we need to double the page order + * for FCoE enabled Rx queues. + */ +static inline unsigned int rnpm_rx_bufsz(struct rnpm_ring *ring) +{ + // 1 rx-desc trans max half page(2048), for jumbo frame sg is needed + // return RNPM_RXBUFFER_MAX; + return RNPM_RXBUFFER_1536 - NET_IP_ALIGN; +} + +/* SG , 1 rx-desc use one page */ +static inline unsigned int rnpm_rx_pg_order(struct rnpm_ring *ring) +{ + /* fixed 1 page */ + /* we don't support 3k buffer */ + return 0; +} +#define rnpm_rx_pg_size(_ring) (PAGE_SIZE << rnpm_rx_pg_order(_ring)) + +struct rnpm_ring_container { + struct rnpm_ring *ring; /* pointer to linked list of rings */ + unsigned long next_update; /* jiffies value of last update */ + unsigned int total_bytes; /* total bytes processed this int */ + unsigned int total_packets; /* total packets processed this int */ + /* record rnpm poll function exec times in one jiffies */ + unsigned int poll_times; + u16 work_limit; /* total work allowed per interrupt */ + u8 count; /* total number of rings in vector */ + u16 itr; /* current ITR/MSIX vector setting for ring */ +}; + +/* iterator for handling rings in ring container */ +#define rnpm_for_each_ring(pos, head) \ + for (pos = (head).ring; pos != NULL; pos = pos->next) + +#define MAX_RX_PACKET_BUFFERS ((adapter->flags & RNPM_FLAG_DCB_ENABLED) ? 8 : 1) +#define MAX_TX_PACKET_BUFFERS MAX_RX_PACKET_BUFFERS + +/* MAX_Q_VECTORS of these are allocated, + * but we only use one per queue-specific vector. + */ +struct rnpm_q_vector { + int new_rx_count; + int old_rx_count; + struct rnpm_adapter *adapter; + int factor; + /* index of q_vector within array, also used for + * finding the bit in EICR and friends that + * represents the vector for this rings + */ + u16 v_idx; + u16 itr; + struct rnpm_ring_container rx, tx; + + struct napi_struct napi; + cpumask_t affinity_mask; + struct irq_affinity_notify affinity_notify; + int numa_node; + struct rcu_head rcu; /* to avoid race with update stats on free */ + + int irq_check_usecs; + struct hrtimer irq_miss_check_timer; // to check irq miss +#define RNPM_IRQ_MISS_HANDLE_DONE ((u32)(1 << 0)) + // #define RNPM_IRQ_VECTOR_SOFT_DISABLE (u32)(1 << 1) + unsigned long flags; + + char name[IFNAMSIZ + 9]; + + /* for dynamic allocation of rings associated with this q_vector */ + struct rnpm_ring ring[0] ____cacheline_internodealigned_in_smp; +}; + +#define RNPM_HWMON_TYPE_LOC 0 +#define RNPM_HWMON_TYPE_TEMP 1 +#define RNPM_HWMON_TYPE_CAUTION 2 +#define RNPM_HWMON_TYPE_MAX 3 +#define RNPM_HWMON_TYPE_NAME 4 + +struct hwmon_attr { + struct device_attribute dev_attr; + struct rnpm_hw *hw; + struct rnpm_thermal_diode_data *sensor; + char name[12]; +}; + +struct hwmon_buff { + struct attribute_group group; + const struct attribute_group *groups[2]; + struct attribute *attrs[RNPM_MAX_SENSORS * 4 + 1]; + struct hwmon_attr hwmon_list[RNPM_MAX_SENSORS * 4]; + unsigned int n_hwmon; +}; + +/* rnpm_test_staterr - tests bits in Rx descriptor status and error fields */ +static inline __le16 rnpm_test_staterr(union rnpm_rx_desc *rx_desc, + const u16 stat_err_bits) +{ + return rx_desc->wb.cmd & cpu_to_le16(stat_err_bits); +} + +static inline __le16 rnpm_get_stat(union rnpm_rx_desc *rx_desc, + const u16 stat_mask) +{ + return rx_desc->wb.cmd & cpu_to_le16(stat_mask); +} + +static inline u16 rnpm_desc_unused(struct rnpm_ring *ring) +{ + u16 ntc = ring->next_to_clean; + u16 ntu = ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} + +static inline u16 rnpm_desc_unused_rx(struct rnpm_ring *ring) +{ + u16 ntc = ring->next_to_clean; + u16 ntu = ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} + +#define RNPM_RX_DESC(R, i) (&(((union rnpm_rx_desc *)((R)->desc))[i])) +#define RNPM_TX_DESC(R, i) (&(((struct rnpm_tx_desc *)((R)->desc))[i])) +#define RNPM_TX_CTXTDESC(R, i) (&(((struct rnpm_tx_ctx_desc *)((R)->desc))[i])) + +#define RNPM_MAX_JUMBO_FRAME_SIZE 9590 /* Maximum Supported Size 9.5KB */ +#define RNPM_MIN_MTU 68 + +#define OTHER_VECTOR 1 +#define NON_Q_VECTORS (OTHER_VECTOR) + +/* default to trying for four seconds */ +#define RNPM_TRY_LINK_TIMEOUT (4 * HZ) + +#define RNPM_MAX_USER_PRIO (8) +#define RNPM_MAX_TCS_NUM (3) +struct rnpm_pfc_cfg { + u8 pfc_max; /* hardware can enabled max pfc channel */ + u8 hw_pfc_map; /* enable the prio channel bit */ + u8 pfc_num; /* at present enabled the pfc-channel num */ + u8 pfc_en; /* enabled the pfc feature or not */ +}; + +struct rnpm_dcb_cfg { + u8 tc_num; + u16 delay; /* pause time */ + u8 dcb_en; /* enabled the dcb feature or not */ + u8 dcbx_mode; + struct rnpm_pfc_cfg pfc_cfg; + + /* statistic info */ + + u64 requests[RNPM_MAX_TCS_NUM]; + u64 indications[RNPM_MAX_TCS_NUM]; + + enum rnpm_fc_mode last_lfc_mode; +}; + +/* board pf adapter */ +struct rnpm_pf_adapter { + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + spinlock_t vlan_setup_lock; + spinlock_t drop_setup_lock; + spinlock_t dummy_setup_lock; + spinlock_t pf_setup_lock; + + struct timer_list service_timer; + struct work_struct service_task; + +#define RNPM_PF_RESET ((u32)(1 << 0)) +#define RNPM_PF_SET_MTU ((u32)(1 << 1)) +#define RNPM_PF_LINK_CHANGE ((u32)(1 << 2)) +#define RNPM_PF_SERVICE_SKIP_HANDLE ((u32)(1 << 3)) + + unsigned long flags; + + struct pci_dev *pdev; + struct rnpm_adapter *adapter[MAX_PORT_NUM]; + bool force_10g_1g_speed_ablity; + int register_sequence[MAX_PORT_NUM]; + int adapter_cnt; + u8 __iomem *hw_bar2; + u8 __iomem *hw_addr; + u8 __iomem *hw_addr4; + u8 __iomem *hw_bar0; + u32 board_type; + u32 port_valid; /* only used in 8 ports */ + u32 port_names; /* only used in 8 ports */ + u32 bd_number; + u8 __iomem *rpu_addr; + u8 rpu_inited; + /* msix table */ + struct msix_entry *msix_entries; + int max_msix_counts[MAX_PORT_NUM]; + int other_irq; + + spinlock_t key_setup_lock; + /* size of RSS Hash Key in bytes */ +#define RNPM_RSS_KEY_SIZE 40 + u8 rss_key[RNPM_RSS_KEY_SIZE]; + u32 rss_key_setup_flag; + u8 default_rx_ring; + /* multicast addr */ + spinlock_t mc_setup_lock; + u32 mcft_size; + u32 mc_filter_type; + u32 mc_location; + /* netdev rx status */ + u32 num_mc_addrs[MAX_PORT_NUM]; + u32 mta_in_use[MAX_PORT_NUM]; +#define RNPM_MAX_MTA 128 + u32 mta_shadow[RNPM_MAX_MTA]; + u32 fctrl[MAX_PORT_NUM]; + /* vlan filter status */ + u32 vlan_filter_status[MAX_PORT_NUM]; + spinlock_t vlan_filter_lock; + u32 vlan_status_true; + /* priv_flags used by mutiports */ + u32 priv_flags; + spinlock_t priv_flags_lock; + + u8 lane_link_status; + struct mutex mbx_lock; + + unsigned long state; + u32 timer_count; + /* just for mailbox use */ + struct rnpm_hw hw; + char name[60]; +}; + +enum priv_bits { + mac_loopback = 0, + switch_loopback = 1, + veb_enable = 4, + padding_enable = 8, + padding_debug_enable = 0x10, +}; + +/* board specific private data structure */ +struct rnpm_adapter { + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + struct rnpm_pf_adapter *pf_adapter; + /* OS defined structs */ + struct net_device *netdev; + bool rm_mode; + bool netdev_registered; + + struct pci_dev *pdev; + bool quit_poll_thread; + struct task_struct *rx_poll_thread; + unsigned long state; + /* this var is used for auto itr modify */ + /* hw not Supported well */ + unsigned long last_moder_packets[MAX_RX_QUEUES]; + unsigned long last_moder_tx_packets; + unsigned long last_moder_bytes[MAX_RX_QUEUES]; + unsigned long last_moder_jiffies; + int last_moder_time[MAX_RX_QUEUES]; + u32 timer_count; + u32 service_count; + + /* only rx itr is Supported */ + u32 rx_usecs; + u32 rx_frames; + u32 tx_usecs; + u32 tx_frames; + u32 pkt_rate_low; + u16 rx_usecs_low; + u32 pkt_rate_high; + u16 rx_usecs_high; + u32 sample_interval; + u32 adaptive_rx_coal; + u32 adaptive_tx_coal; + u32 auto_rx_coal; + + int lane; + int speed; + + int napi_budge; + + union { + int phy_addr; + struct { + u8 mod_abs : 1; + u8 fault : 1; + u8 tx_dis : 1; + u8 los : 1; + } sfp; + }; + + struct { + u32 main; + u32 pre; + u32 post; + u32 tx_boost; + } si; + + u8 an : 1; + u8 fec : 1; + u8 link_traing : 1; + u8 duplex : 1; + + /* Some features need tri-state capability, + * thus the additional *_CAPABLE flags. + */ + u32 vf_num_for_pf; + u32 flags; +#define RNPM_FLAG_MSI_CAPABLE ((u32)(1 << 0)) +#define RNPM_FLAG_MSI_ENABLED ((u32)(1 << 1)) +#define RNPM_FLAG_MSIX_CAPABLE ((u32)(1 << 2)) +#define RNPM_FLAG_MSIX_ENABLED ((u32)(1 << 3)) +#define RNPM_FLAG_RX_1BUF_CAPABLE ((u32)(1 << 4)) +#define RNPM_FLAG_RX_PS_CAPABLE ((u32)(1 << 5)) +#define RNPM_FLAG_RX_PS_ENABLED ((u32)(1 << 6)) +#define RNPM_FLAG_IN_NETPOLL ((u32)(1 << 7)) +#define RNPM_FLAG_DCA_ENABLED ((u32)(1 << 8)) +#define RNPM_FLAG_DCA_CAPABLE ((u32)(1 << 9)) +#define RNPM_FLAG_IMIR_ENABLED ((u32)(1 << 10)) +#define RNPM_FLAG_MQ_CAPABLE ((u32)(1 << 11)) +#define RNPM_FLAG_DCB_ENABLED ((u32)(1 << 12)) +#define RNPM_FLAG_VMDQ_CAPABLE ((u32)(1 << 13)) +#define RNPM_FLAG_VMDQ_ENABLED ((u32)(1 << 14)) +#define RNPM_FLAG_FAN_FAIL_CAPABLE ((u32)(1 << 15)) +#define RNPM_FLAG_NEED_LINK_UPDATE ((u32)(1 << 16)) +#define RNPM_FLAG_NEED_LINK_CONFIG ((u32)(1 << 17)) +#define RNPM_FLAG_FDIR_HASH_CAPABLE ((u32)(1 << 18)) +#define RNPM_FLAG_FDIR_PERFECT_CAPABLE ((u32)(1 << 19)) +#define RNPM_FLAG_FCOE_CAPABLE ((u32)(1 << 20)) +#define RNPM_FLAG_FCOE_ENABLED ((u32)(1 << 21)) +#define RNPM_FLAG_SRIOV_CAPABLE ((u32)(1 << 22)) +#define RNPM_FLAG_SRIOV_ENABLED ((u32)(1 << 23)) +#define RNPM_FLAG_MUTIPORT_ENABLED ((u32)(1 << 24)) + /* only in mutiport mode */ +#define RNPM_FLAG_RXHASH_DISABLE ((u32)(1 << 25)) +#define RNPM_FLAG_VXLAN_OFFLOAD_CAPABLE ((u32)(1 << 26)) +#define RNPM_FLAG_VXLAN_OFFLOAD_ENABLE ((u32)(1 << 27)) +#define RNPM_FLAG_SWITCH_LOOPBACK_EN ((u32)(1 << 28)) + + u32 flags2; +#define RNPM_FLAG2_RSC_CAPABLE ((u32)(1 << 0)) +#define RNPM_FLAG2_RSC_ENABLED ((u32)(1 << 1)) +#define RNPM_FLAG2_TEMP_SENSOR_CAPABLE ((u32)(1 << 2)) +#define RNPM_FLAG2_TEMP_SENSOR_EVENT ((u32)(1 << 3)) +#define RNPM_FLAG2_SEARCH_FOR_SFP ((u32)(1 << 4)) +#define RNPM_FLAG2_SFP_NEEDS_RESET ((u32)(1 << 5)) +#define RNPM_FLAG2_RESET_REQUESTED ((u32)(1 << 6)) +#define RNPM_FLAG2_FDIR_REQUIRES_REINIT ((u32)(1 << 7)) +#define RNPM_FLAG2_RSS_FIELD_IPV4_UDP ((u32)(1 << 8)) +#define RNPM_FLAG2_RSS_FIELD_IPV6_UDP ((u32)(1 << 9)) +#define RNPM_FLAG2_PTP_ENABLED ((u32)(1 << 10)) +#define RNPM_FLAG2_PTP_PPS_ENABLED ((u32)(1 << 11)) +#define RNPM_FLAG2_BRIDGE_MODE_VEB ((u32)(1 << 12)) +#define RNPM_FLAG2_VLAN_STAGS_ENABLED ((u32)(1 << 13)) +#define RNPM_FLAG2_UDP_TUN_REREG_NEEDED ((u32)(1 << 14)) +#define RNPM_FLAG2_TX_RATE_SETUP ((u32)(1 << 14)) + u32 flags_feature; +#define RNPM_FLAG_DELAY_UPDATE_VLAN_FILTER ((u32)(1 << 0)) +#define RNPM_FLAG_DELAY_UPDATE_VLAN_TABLE ((u32)(1 << 1)) +#define RNPM_FLAG_DELAY_UPDATE_MUTICAST_TABLE ((u32)(1 << 1)) + + u32 priv_flags; +#define RNPM_PRIV_FLAG_MAC_LOOPBACK BIT(0) +#define RNPM_PRIV_FLAG_SWITCH_LOOPBACK BIT(1) +#define RNPM_PRIV_FLAG_VEB_ENABLE BIT(2) +#define RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH BIT(3) +#define RNPM_PRIV_FLAG_PADDING_DEBUG BIT(4) +#define RNPM_PRIV_FLAG_PTP_DEBUG BIT(5) +#define RNPM_PRIV_FLAG_SIMUATE_DOWN BIT(6) +#define RNPM_PRIV_FLAG_TO_RPU BIT(7) +#define RNPM_PRIV_FLAG_LEN_ERR BIT(8) +#define RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN BIT(9) +#define RNPM_PRIV_FLAG_TX_PADDING BIT(13) +#define RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY BIT(14) +#define RNPM_PRIV_FLAG_LLDP_EN_STAT BIT(15) + + /* Tx fast path data */ + unsigned int num_tx_queues; + unsigned int max_ring_pair_counts; + unsigned int max_msix_counts; + u16 tx_work_limit; + __be16 vxlan_port; + __be16 geneve_port; + /* Rx fast path data */ + int num_rx_queues; + u16 rx_itr_setting; + u32 eth_queue_idx; + u32 max_rate[MAX_TX_QUEUES]; + /* TX */ + struct rnpm_ring *tx_ring[MAX_TX_QUEUES] ____cacheline_aligned_in_smp; + int tx_ring_item_count; + + u64 restart_queue; + u64 lsc_int; + u32 tx_timeout_count; + + /* RX */ + struct rnpm_ring *rx_ring[MAX_RX_QUEUES]; + int rx_ring_item_count; + + u64 hw_csum_rx_error; + u64 hw_csum_rx_good; + u64 hw_rx_no_dma_resources; + u64 rsc_total_count; + u64 rsc_total_flush; + u64 non_eop_descs; + u32 alloc_rx_page_failed; + u32 alloc_rx_buff_failed; + + int num_other_vectors; + struct rnpm_q_vector *q_vector[MAX_Q_VECTORS]; + /*used for IEEE 1588 ptp clock start*/ + const struct rnpm_hwtimestamp *hwts_ops; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_ops; + struct sk_buff *ptp_tx_skb; + struct hwtstamp_config tstamp_config; + u32 ptp_config_value; + spinlock_t ptp_lock; /* Used to protect the SYSTIME registers. */ + + u64 clk_ptp_rate; /*uint is HZ 1MHz=1 000 000Hz*/ + u32 sub_second_inc; + u32 systime_flags; + struct timespec64 ptp_prev_hw_time; + unsigned int default_addend; + bool ptp_tx_en; + bool ptp_rx_en; + + struct work_struct tx_hwtstamp_work; + unsigned long tx_hwtstamp_start; + unsigned long tx_hwtstamp_skipped; + unsigned long tx_timeout_factor; + u64 tx_hwtstamp_timeouts; + /*used for IEEE 1588 ptp clock end */ + + /* DCB parameters */ + struct rnpm_dcb_cfg dcb_cfg; + u8 prio_tc_map[RNPM_MAX_USER_PRIO]; + u8 num_tc; + + int num_q_vectors; /* current number of q_vectors for device */ + int max_q_vectors; /* true count of q_vectors for device */ + struct rnpm_ring_feature ring_feature[RING_F_ARRAY_SIZE]; + struct msix_entry *msix_entries; + + u32 test_icr; + struct rnpm_ring test_tx_ring; + struct rnpm_ring test_rx_ring; + + /* structs defined in rnpm_hw.h */ + struct rnpm_hw hw; + u16 msg_enable; + struct rnpm_hw_stats hw_stats; + + u64 tx_busy; + + u32 link_speed; + bool link_up; + unsigned long link_check_timeout; + + struct timer_list service_timer; + struct work_struct service_task; + + /* fdir relative */ + struct hlist_head fdir_filter_list; + unsigned long fdir_overflow; /* number of times ATR was backed off */ + union rnpm_atr_input fdir_mask; + int fdir_mode; + int fdir_filter_count; + int layer2_count; + int layer2_count_max; + int layer2_offset; + int tuple_5_count; + int tuple_5_count_max; + int tuple_5_offset; + u32 fdir_pballoc; //total count + u32 atr_sample_rate; + spinlock_t fdir_perfect_lock; + + u32 wol; + + u16 bd_number; + u16 vector_off; + + u16 eeprom_verh; + u16 eeprom_verl; + u16 eeprom_cap; + + u16 stags_vid; + + u32 interrupt_event; + u32 led_reg; + + /* maintain */ + char *maintain_buf; + int maintain_buf_len; + void *maintain_dma_buf; + dma_addr_t maintain_dma_phy; + int maintain_dma_size; + int maintain_in_bytes; + + /* SR-IOV */ + DECLARE_BITMAP(active_vfs, RNPM_MAX_VF_FUNCTIONS); + unsigned int num_vfs; + struct vf_data_storage *vfinfo; + int vf_rate_link_speed; + struct vf_macvlans vf_mvs; + struct vf_macvlans *mv_list; + + u32 timer_event_accumulator; + u32 vferr_refcount; + struct kobject *info_kobj; + struct hwmon_buff *rnpm_hwmon_buff; + +#ifdef CONFIG_DEBUG_FS + struct dentry *rnpm_dbg_adapter; +#endif /*CONFIG_DEBUG_FS*/ + + u8 default_up; + //u8 veb_vfnum; + + u8 port; /* nr_pf_port: 0 or 1 */ + u8 portid_of_card; /* port num in card*/ +#define RNPM_MAX_RETA_ENTRIES 512 + u8 rss_indir_tbl[RNPM_MAX_RETA_ENTRIES]; + u32 rss_tbl_setup_flag; + + /* #define RNPM_RSS_KEY_SIZE 40 + * u8 rss_key[RNPM_RSS_KEY_SIZE]; + * u32 rss_key_setup_flag; + * struct rnpm_info* info; + */ + bool dma2_in_1pf; + + u8 uc_off; + u8 uc_num; + + char name[60]; +}; + +struct rnpm_fdir_filter { + struct hlist_node fdir_node; + union rnpm_atr_input filter; + u16 sw_idx; + u16 hw_idx; + u32 vf_num; + u64 action; +}; + +enum rnpm_state_t { + __RNPM_TESTING, + __RNPM_RESETTING, + __RNPM_DOWN, + __RNPM_SERVICE_SCHED, + __RNPM_IN_SFP_INIT, + __RNPM_READ_I2C, + __RNPM_PTP_TX_IN_PROGRESS, + __RNPM_REMOVING, +}; + +struct rnpm_cb { + union { /* Union defining head/tail partner */ + struct sk_buff *head; + struct sk_buff *tail; + }; + dma_addr_t dma; + u16 append_cnt; + bool page_released; +}; +#define RNPM_CB(skb) ((struct rnpm_cb *)(skb)->cb) + +enum rnpm_boards { + board_n10_709_1pf_2x10G, // not support + board_n10_vu440_1pf_2x10G, // not support + board_vu440_2x10G, + board_vu440_2x40G, + board_n10_uv3p_1pf_2x10G, // not support + board_vu440_4x10G, + board_vu440_8x10G, + board_n10, + board_n400_4x1G, +}; + +#ifdef CONFIG_RNPM_DCB +extern const struct dcbnl_rtnl_ops dcbnl_ops; +#endif + +extern char rnpm_driver_name[]; +extern const char rnpm_driver_version[]; +extern struct rnpm_info rnpm_n10_info; +extern struct rnpm_info rnpm_n400_4x1G_info; + +extern void rnpm_up(struct rnpm_adapter *adapter); +extern void rnpm_down(struct rnpm_adapter *adapter); +extern void rnpm_reinit_locked(struct rnpm_adapter *adapter); +extern void rnpm_reset(struct rnpm_adapter *adapter); +extern void rnpm_set_ethtool_ops(struct net_device *netdev); +extern int rnpm_setup_rx_resources(struct rnpm_ring *ring, + struct rnpm_adapter *adapter); +extern int rnpm_setup_tx_resources(struct rnpm_ring *ring, + struct rnpm_adapter *adapter); +extern void rnpm_free_rx_resources(struct rnpm_ring *ring); +extern void rnpm_free_tx_resources(struct rnpm_ring *ring); +extern void rnpm_configure_rx_ring(struct rnpm_adapter *adapter, + struct rnpm_ring *ring); +extern void rnpm_configure_tx_ring(struct rnpm_adapter *adapter, + struct rnpm_ring *ring); +extern void rnpm_disable_rx_queue(struct rnpm_adapter *adapter, + struct rnpm_ring *ring); +extern void rnpm_update_stats(struct rnpm_adapter *adapter); +extern int rnpm_init_interrupt_scheme(struct rnpm_adapter *adapter); +extern int rnpm_wol_supported(struct rnpm_adapter *adapter, u16 device_id, + u16 subdevice_id); +extern void rnpm_clear_interrupt_scheme(struct rnpm_adapter *adapter); +extern netdev_tx_t rnpm_xmit_frame_ring(struct sk_buff *skb, + struct rnpm_adapter *adapter, + struct rnpm_ring *ring); +extern void rnpm_unmap_and_free_tx_resource(struct rnpm_ring *ring, + struct rnpm_tx_buffer *buffer); +extern void rnpm_alloc_rx_buffers(struct rnpm_ring *ring, u16 cnt); +extern int rnpm_poll(struct napi_struct *napi, int budget); +extern int ethtool_ioctl(struct ifreq *ifr); +extern s32 rnpm_reinit_fdir_tables_n10(struct rnpm_hw *hw); +extern s32 rnpm_init_fdir_signature_n10(struct rnpm_hw *hw, u32 fdirctrl); +extern s32 rnpm_init_fdir_perfect_n10(struct rnpm_hw *hw, u32 fdirctrl); +extern s32 rnpm_fdir_add_signature_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_hash_dword input, + union rnpm_atr_hash_dword common, + u8 queue); + +extern void rnpm_release_hw_control(struct rnpm_adapter *adapter); +extern void rnpm_get_hw_control(struct rnpm_adapter *adapter); +extern s32 rnpm_fdir_set_input_mask_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input_mask); +extern s32 rnpm_fdir_write_perfect_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input, + u16 soft_id, u8 queue); +extern s32 rnpm_fdir_erase_perfect_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input, + u16 soft_id); +extern void rnpm_atr_compute_perfect_hash_n10(union rnpm_atr_input *input, + union rnpm_atr_input *mask); +extern bool rnpm_verify_lesm_fw_enabled_n10(struct rnpm_hw *hw); +extern void rnpm_set_rx_mode(struct net_device *netdev); +#ifdef CONFIG_RNPM_DCB +extern void rnpm_set_rx_drop_en(struct rnpm_adapter *adapter); +#endif +extern int rnpm_setup_tx_maxrate(void __iomem *ioaddr, + struct rnpm_ring *tx_ring, u64 max_rate, + int samples_1sec); +extern int rnpm_setup_tc(struct net_device *dev, u8 tc); +extern int rnpm_open(struct net_device *netdev); +extern int rnpm_close(struct net_device *netdev); +extern void rnpm_service_event_schedule(struct rnpm_adapter *adapter); +void rnpm_tx_ctxtdesc(struct rnpm_ring *tx_ring, u32 mss_len_vf_num, + u32 inner_vlan_tunnel_len, u32 type_tucmd); +void rnpm_maybe_tx_ctxtdesc(struct rnpm_ring *tx_ring, + struct rnpm_tx_buffer *first, u32 type_tucmd); +extern void rnpm_store_reta(struct rnpm_adapter *adapter); +extern void rnpm_store_key(struct rnpm_pf_adapter *pf_adapter); +extern int rnpm_init_rss_key(struct rnpm_pf_adapter *adapter); +extern int rnpm_init_rss_table(struct rnpm_adapter *adapter); +extern void rnpm_setup_dma_rx(struct rnpm_adapter *adapter, int count_in_dw); +extern s32 rnpm_fdir_write_perfect_filter(int fdir_mode, struct rnpm_hw *hw, + union rnpm_atr_input *filter, + u16 hw_id, u8 queue); +extern s32 rnpm_fdir_erase_perfect_filter(int fdir_mode, struct rnpm_hw *hw, + union rnpm_atr_input *input, + u16 hw_id); +extern u32 rnpm_rss_indir_tbl_entries(struct rnpm_adapter *adapter); +extern u32 rnpm_tx_desc_unused_sw(struct rnpm_ring *tx_ring); +extern u32 rnpm_tx_desc_unused_hw(struct rnpm_hw *hw, + struct rnpm_ring *tx_ring); +extern s32 rnpm_disable_rxr_maxrate(struct net_device *netdev, u8 queue_index); +extern s32 rnpm_enable_rxr_maxrate(struct net_device *netdev, u8 queue_index, + u32 maxrate); +extern u32 rnpm_rx_desc_used_hw(struct rnpm_hw *hw, struct rnpm_ring *rx_ring); +extern void rnpm_do_reset(struct net_device *netdev); +#ifdef CONFIG_RNPM_HWMON +extern void rnpm_sysfs_exit(struct rnpm_adapter *adapter); +extern int rnpm_sysfs_init(struct rnpm_adapter *adapter, int port); +#endif /* CONFIG_RNPM_HWMON */ +#ifdef CONFIG_DEBUG_FS +extern void rnpm_dbg_adapter_init(struct rnpm_adapter *adapter); +extern void rnpm_dbg_adapter_exit(struct rnpm_adapter *adapter); +extern void rnpm_dbg_init(void); +extern void rnpm_dbg_exit(void); +#else +static inline void rnpm_dbg_adapter_init(struct rnpm_adapter *adapter) +{ +} +static inline void rnpm_dbg_adapter_exit(struct rnpm_adapter *adapter) +{ +} +static inline void rnpm_dbg_init(void) +{ +} +static inline void rnpm_dbg_exit(void) +{ +} +#endif /* CONFIG_DEBUG_FS */ +static inline struct netdev_queue *txring_txq(const struct rnpm_ring *ring) +{ + return netdev_get_tx_queue(ring->netdev, ring->queue_index); +} + +extern void rnpm_ptp_init(struct rnpm_adapter *adapter); +extern void rnpm_ptp_stop(struct rnpm_adapter *adapter); +extern void rnpm_ptp_overflow_check(struct rnpm_adapter *adapter); +extern void rnpm_ptp_rx_hang(struct rnpm_adapter *adapter); +extern void __rnpm_ptp_rx_hwtstamp(struct rnpm_q_vector *q_vector, + struct sk_buff *skb); +static inline void rnpm_ptp_rx_hwtstamp(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + if (unlikely(!rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_PTP))) + return; + + //__rnpm_ptp_rx_hwtstamp(rx_ring->q_vector, skb); + + /* Update the last_rx_timestamp timer in order to enable watchdog check + * for error case of latched timestamp on a dropped packet. + */ + rx_ring->last_rx_timestamp = jiffies; +} + +static inline int ignore_veb_pkg_err(struct rnpm_adapter *adapter, + union rnpm_rx_desc *rx_desc) +{ +#ifdef RNPM_IOV_VEB_BUG_NOT_FIXED + if (unlikely((adapter->flags & RNPM_FLAG_SRIOV_ENABLED) && + (cpu_to_le16(rx_desc->wb.mark) & VEB_VF_PKG))) { + return 1; + } +#endif + return 0; +} + +int rnpm_update_ethtool_fdir_entry(struct rnpm_adapter *adapter, + struct rnpm_fdir_filter *input, u16 sw_idx); + +static inline bool rnpm_is_pf1(struct pci_dev *pdev) +{ + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(pdev); + /* n10 read this from bar0 */ + u16 vf_num = -1; + u32 pfvfnum_reg; +#define PF_NUM_REG_N10 (0x75f000) + pfvfnum_reg = (PF_NUM_REG_N10 & (pci_resource_len(pdev, 0) - 1)); + vf_num = readl(pf_adapter->hw_bar0 + pfvfnum_reg); +#define VF_NUM_MASK_TEMP (0x400) +#define VF_NUM_OFF (4) + return !!((vf_num & VF_NUM_MASK_TEMP) >> VF_NUM_OFF); +} + +extern void rnpm_service_task(struct work_struct *work); +extern void rnpm_sysfs_exit(struct rnpm_adapter *adapter); +extern int rnpm_sysfs_init(struct rnpm_adapter *adapter, int port); + +#ifdef CONFIG_PCI_IOV +void rnpm_sriov_reinit(struct rnpm_adapter *adapter); +#endif + +#define SET_BIT(n, var) (var = (var | (1 << n))) +#define CLR_BIT(n, var) (var = (var & (~(1 << n)))) +#define CHK_BIT(n, var) (var & (1 << n)) + +#define RNPM_RX_DMA_ATTR (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) + +static inline bool rnpm_removed(void __iomem *addr) +{ + return unlikely(!addr); +} +#define RNPM_REMOVED(a) rnpm_removed(a) +static inline bool rnpm_port_is_valid(struct rnpm_pf_adapter *pf_adapter, int i) +{ + bool b = false; + + if (i >= MAX_PORT_NUM) { + //rnpm_dbg("Port number cannot over MAX_PORT_NUM!\n"); + return false; + } + b = !!(pf_adapter->port_valid & (1 << i)); + + return b; +} + +int rnpm_set_clause73_autoneg_enable(struct net_device *netdev, int enable); +int rnpm_card_partially_supported_10g_1g_sfp(struct rnpm_pf_adapter *pf_adapter); + +#define RNPM_FW_VERSION_NEW_ETHTOOL 0x00050010 +static inline bool rnpm_fw_is_old_ethtool(struct rnpm_hw *hw) +{ + return hw->fw_version >= RNPM_FW_VERSION_NEW_ETHTOOL ? false : true; +} + +static inline int Hamming_weight_1(u32 n) +{ + int count_ = 0; + + while (n != 0) { + n &= (n - 1); + count_++; + } + return count_; +} + +#define RNPM_WOL_GET_SUPPORTED(adapter) \ + (!!(adapter->wol & (BIT(0) << adapter->port))) +#define RNPM_WOL_GET_STATUS(adapter) \ + (!!(adapter->wol & (BIT(4) << adapter->port))) +#define RNPM_WOL_SET_SUPPORTED(adapter) \ + (adapter->wol |= BIT(0) << adapter->port) +#define RNPM_WOL_SET_STATUS(adapter) (adapter->wol |= BIT(4) << adapter->port) +#define RNPM_WOL_CLEAR_STATUS(adapter) \ + (adapter->wol &= ~(BIT(4) << adapter->port)) + +#endif /* _RNPM_H_ */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_common.c b/drivers/net/ethernet/mucse/rnpm/rnpm_common.c new file mode 100644 index 000000000000..ea8353fbdf5a --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_common.c @@ -0,0 +1,2318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include +#include + +#include "rnpm.h" +#include "rnpm_common.h" +#include "rnpm_phy.h" +#include "rnpm_mbx_fw.h" + +static s32 rnpm_acquire_eeprom(struct rnpm_hw *hw); +static s32 rnpm_get_eeprom_semaphore(struct rnpm_hw *hw); +static void rnpm_release_eeprom_semaphore(struct rnpm_hw *hw); +static s32 rnpm_ready_eeprom(struct rnpm_hw *hw); +static void rnpm_standby_eeprom(struct rnpm_hw *hw); +static void rnpm_shift_out_eeprom_bits(struct rnpm_hw *hw, u16 data, u16 count); +static u16 rnpm_shift_in_eeprom_bits(struct rnpm_hw *hw, u16 count); +static void rnpm_raise_eeprom_clk(struct rnpm_hw *hw, u32 *eec); +static void rnpm_lower_eeprom_clk(struct rnpm_hw *hw, u32 *eec); +static void rnpm_release_eeprom(struct rnpm_hw *hw); + +static s32 rnpm_mta_vector(int mode, u8 *mc_addr); +static s32 rnpm_poll_eerd_eewr_done(struct rnpm_hw *hw, u32 ee_reg); +static s32 rnpm_read_eeprom_buffer_bit_bang(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data); +static s32 rnpm_write_eeprom_buffer_bit_bang(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data); +static s32 rnpm_detect_eeprom_page_size_generic(struct rnpm_hw *hw, u16 offset); +static s32 rnpm_disable_pcie_master(struct rnpm_hw *hw); + +unsigned int rnpm_loglevel; +module_param(rnpm_loglevel, uint, 0600); + +/** + * rnpm_device_supports_autoneg_fc - Check if phy supports autoneg flow + * control + * @hw: pointer to hardware structure + * + * There are several phys that do not support autoneg flow control. This + * function check the device id to see if the associated phy supports + * autoneg flow control. + **/ +bool rnpm_device_supports_autoneg_fc(struct rnpm_hw *hw) +{ + bool supported = false; + + if (hw->is_sgmii == 0) + return false; + + switch (hw->phy.media_type) { + case rnpm_media_type_fiber: + break; + case rnpm_media_type_backplane: + break; + case rnpm_media_type_copper: + /* only some copper devices support flow control autoneg */ + supported = true; + break; + default: + break; + } + + return supported; +} + +/** + * rnpm_setup_fc - Set up flow control + * @hw: pointer to hardware structure + * + * Called at init time to set up flow control. + **/ +s32 rnpm_setup_fc(struct rnpm_hw *hw) +{ + s32 ret = 0; + u16 pause_bits = 0; + u16 value; + + /* phy pause */ + if (!hw->is_sgmii) + goto out; + + switch (hw->fc.requested_mode) { + case rnpm_fc_none: + /* Flow control completely disabled by software override. */ + break; + case rnpm_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + pause_bits |= BIT(11); + break; + case rnpm_fc_rx_pause: + /* Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE, as such we fall + * through to the fc_full statement. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + case rnpm_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + pause_bits |= BIT(11) | BIT(10); + break; + default: + hw_dbg(hw, "Flow control phy param set incorrectly\n"); + ret = RNPM_ERR_CONFIG; + goto out; + } + hw->phy.ops.read_reg(hw, 4, 0, &value); + value &= ~0xC00; + value |= pause_bits; + hw->phy.ops.write_reg(hw, 4, 0, value); + +out: + return ret; +} + +/** + * rnpm_start_hw_generic - Prepare hardware for Tx/Rx + * @hw: pointer to hardware structure + * + * Starts the hardware by filling the bus info structure and media type, clears + * all on chip counters, initializes receive address registers, multicast + * table, VLAN filter table, calls routine to set up link and flow control + * settings, and leaves transmit and receive units disabled and uninitialized + **/ +s32 rnpm_start_hw_generic(struct rnpm_hw *hw) +{ + // u32 ctrl_ext; + +#ifdef UV3P_1PF + return 0; +#endif + + /* Set the media type */ + hw->phy.media_type = hw->mac.ops.get_media_type(hw); + + /* Identify the PHY */ + hw->phy.ops.identify(hw); + + /* Clear the VLAN filter table */ + /* maybe mistalbe here in mutiport*/ + hw->mac.ops.clear_vfta(hw); + + /* Clear statistics registers */ + hw->mac.ops.clear_hw_cntrs(hw); + + /* Setup flow control */ + hw->mac.ops.setup_fc(hw); + /* Clear adapter stopped flag */ + hw->adapter_stopped = false; + return 0; +} + +/** + * rnpm_start_hw_gen2 - Init sequence for common device family + * @hw: pointer to hw structure + * + * Performs the init sequence common to the second generation + * of 10 GbE devices. + * Devices in the second generation: + * n10 + * X540 + **/ +s32 rnpm_start_hw_gen2(struct rnpm_hw *hw) +{ + u32 i; + // u32 regval; + + /* Clear the rate limiters */ + for (i = 0; i < hw->mac.max_tx_queues; i++) { + ; + ; + } + + /* Disable relaxed ordering */ + for (i = 0; i < hw->mac.max_tx_queues; i++) { + ; + ; + } + + for (i = 0; i < hw->mac.max_rx_queues; i++) { + ; + ; + } + return 0; +} + +/** + * rnpm_init_hw_generic - Generic hardware initialization + * @hw: pointer to hardware structure + * + * Initialize the hardware by resetting the hardware, filling the bus info + * structure and media type, clears all on chip counters, initializes receive + * address registers, multicast table, VLAN filter table, calls routine to set + * up link and flow control settings, and leaves transmit and receive units + * disabled and uninitialized + **/ +s32 rnpm_init_hw_generic(struct rnpm_hw *hw) +{ + s32 status; + + /* Reset the hardware */ + status = hw->mac.ops.reset_hw(hw); + + if (status == 0) { + /* Start the HW */ + status = hw->mac.ops.start_hw(hw); + } + + return status; +} + +void rnpm_reset_msix_table_generic(struct rnpm_hw *hw) +{ + int i; + /* reset NIC_RING_VECTOR table to 0 */ + for (i = 0; i < 128; i++) + rnpm_wr_reg(hw->ring_msix_base + RING_VECTOR(i), 0); +} + +/** + * rnpm_clear_hw_cntrs_generic - Generic clear hardware counters + * @hw: pointer to hardware structure + * + * Clears all hardware statistics counters by reading them from the hardware + * Statistics counters are clear on read. + **/ +s32 rnpm_clear_hw_cntrs_generic(struct rnpm_hw *hw) +{ + struct rnpm_adapter *adapter = + container_of(hw, struct rnpm_adapter, hw); + struct net_device_stats *net_stats = &adapter->netdev->stats; + + int port = adapter->port; + + hw->err_pkts_init.wdt[port] = rd32(hw, RNPM_RXTRANS_WDT_ERR_PKTS(port)); + hw->err_pkts_init.code[port] = + rd32(hw, RNPM_RXTRANS_CODE_ERR_PKTS(port)); + hw->err_pkts_init.crc[port] = rd32(hw, RNPM_RXTRANS_CRC_ERR_PKTS(port)); + hw->err_pkts_init.slen[port] = + rd32(hw, RNPM_RXTRANS_SLEN_ERR_PKTS(port)); + hw->err_pkts_init.glen[port] = + rd32(hw, RNPM_RXTRANS_GLEN_ERR_PKTS(port)); + hw->err_pkts_init.iph[port] = rd32(hw, RNPM_RXTRANS_IPH_ERR_PKTS(port)); + hw->err_pkts_init.len[port] = rd32(hw, RNPM_RXTRANS_LEN_ERR_PKTS(port)); + hw->err_pkts_init.cut[port] = rd32(hw, RNPM_RXTRANS_CUT_ERR_PKTS(port)); + hw->err_pkts_init.drop[port] = rd32(hw, RNPM_RXTRANS_DROP_PKTS(port)); + hw->err_pkts_init.csum[port] = + rd32(hw, RNPM_RXTRANS_CSUM_ERR_PKTS(port)); + hw->err_pkts_init.scsum[port] = 0; + net_stats->rx_crc_errors = 0; + net_stats->rx_errors = 0; + net_stats->rx_dropped = 0; + + return 0; +} + +/** + * rnpm_read_pba_string_generic - Reads part number string from EEPROM + * @hw: pointer to hardware structure + * @pba_num: stores the part number string from the EEPROM + * @pba_num_size: part number string buffer length + * + * Reads the part number string from the EEPROM. + **/ +s32 rnpm_read_pba_string_generic(struct rnpm_hw *hw, u8 *pba_num, + u32 pba_num_size) +{ + return 0; +} + +s32 rnpm_get_permtion_mac_addr(struct rnpm_hw *hw, u8 *mac_addr) +{ + // u32 v; + // struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + int err = 0; + +#ifdef NO_MBX_VERSION + // TRACE(); + +#ifdef FIX_MAC_TEST + v = 0x00004E46; +#else + v = rd32(hw, RNPM_TOP_MAC_OUI); +#endif + mac_addr[0] = (u8)(v >> 16); + mac_addr[1] = (u8)(v >> 8); + mac_addr[2] = (u8)(v >> 0); + +#ifdef FIX_MAC_TEST + v = 0x00032F10 + rnpm_is_pf1(hw->pdev); +#else + v = rd32(hw, RNPM_TOP_MAC_SN); +#endif + + mac_addr[3] = (u8)(v >> 16); + mac_addr[4] = (u8)(v >> 8); +#ifdef TEMP_MAC_TEST + mac_addr[5] = (u8)(v >> 0) + hw->num * 2; +#else + mac_addr[5] = (u8)(v >> 0) + hw->num; +#endif +#else + err = rnpm_fw_get_macaddr(hw, hw->pfvfnum, mac_addr, hw->nr_lane); + if (err || !is_valid_ether_addr(mac_addr)) { + dbg("generate ramdom macaddress...\n"); + eth_random_addr(mac_addr); + } +#endif + + hw->mac.mac_flags |= RNPM_FLAGS_INIT_MAC_ADDRESS; + dbg("%s mac:%pM\n", __func__, mac_addr); + return 0; +} + +/** + * rnpm_get_mac_addr_generic - Generic get MAC address + * @hw: pointer to hardware structure + * @mac_addr: Adapter MAC address + * + * Reads the adapter's MAC address from first Receive Address Register (RAR0) + * A reset of the adapter must be performed prior to calling this function + * in order for the MAC address to have been loaded from the EEPROM into RAR0 + **/ +s32 rnpm_get_mac_addr_generic(struct rnpm_hw *hw, u8 *mac_addr) +{ + u32 rar_high, rar_low, i; + // struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + + rar_high = rd32(hw, RNPM_ETH_RAR_RH(0)); + rar_low = rd32(hw, RNPM_ETH_RAR_RL(0)); + for (i = 0; i < 4; i++) + mac_addr[i] = (u8)(rar_low >> (i * 8)); + for (i = 0; i < 2; i++) + mac_addr[i + 4] = (u8)(rar_high >> (i * 8)); + mac_addr[5] += hw->num; + return 0; +} + +/** + * rnpm_stop_adapter_generic - Generic stop Tx/Rx units + * @hw: pointer to hardware structure + * + * Sets the adapter_stopped flag within rnpm_hw struct. Clears interrupts, + * disables transmit and receive units. The adapter_stopped flag is used by + * the shared code and drivers to determine if the adapter is in a stopped + * state and should not touch the hardware. + **/ +s32 rnpm_stop_adapter_generic(struct rnpm_hw *hw) +{ + // u32 reg_val; + u16 i; + + /* Set the adapter_stopped flag so other driver functions stop + * touching the hardware + */ + hw->adapter_stopped = true; + + /* Disable the receive unit */ + + /* Clear any pending interrupts, flush previous writes */ + + /* Disable the transmit unit. Each queue must be disabled. */ + for (i = 0; i < hw->mac.max_tx_queues; i++) { + /* Clear interrupt mask to stop interrupts from + * being generated + */ + wr32(hw, RNPM_DMA_INT_CLR(i), 0x3); + // wr32(hw, RNPM_DMA_TX_START(i), 0); + } + + /* Disable the receive unit by stopping each queue */ + for (i = 0; i < hw->mac.max_rx_queues; i++) + wr32(hw, RNPM_DMA_RX_START(i), 0); + + /* flush all queues disables */ + usleep_range(1000, 2000); + + /* Prevent the PCI-E bus from hanging by disabling PCI-E master + * access and verify no pending requests + */ + return rnpm_disable_pcie_master(hw); +} + +/** + * rnpm_led_on_generic - Turns on the software controllable LEDs. + * @hw: pointer to hardware structure + * @index: led number to turn on + **/ +s32 rnpm_led_on_generic(struct rnpm_hw *hw, u32 index) +{ + /* To turn on the LED, set mode to ON. */ + + return 0; +} + +/** + * rnpm_led_off_generic - Turns off the software controllable LEDs. + * @hw: pointer to hardware structure + * @index: led number to turn off + **/ +s32 rnpm_led_off_generic(struct rnpm_hw *hw, u32 index) +{ + /* To turn off the LED, set mode to OFF. */ + + return 0; +} + +/** + * rnpm_init_eeprom_params_generic - Initialize EEPROM params + * @hw: pointer to hardware structure + * + * Initializes the EEPROM parameters rnpm_eeprom_info within the + * rnpm_hw struct in order to set up EEPROM access. + **/ +s32 rnpm_init_eeprom_params_generic(struct rnpm_hw *hw) +{ + // struct rnpm_eeprom_info *eeprom = &hw->eeprom; + // u32 eec; + // u16 eeprom_size; + + return 0; +} + +/** + * rnpm_write_eeprom_buffer_bit_bang_generic - Write EEPROM using bit-bang + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to write + * @words: number of words + * @data: 16 bit word(s) to write to EEPROM + * + * Reads 16 bit word(s) from EEPROM through bit-bang method + **/ +s32 rnpm_write_eeprom_buffer_bit_bang_generic(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_write_eeprom_buffer_bit_bang - Writes 16 bit word(s) to EEPROM + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be written to + * @words: number of word(s) + * @data: 16 bit word(s) to be written to the EEPROM + * + * If rnpm_eeprom_update_checksum is not called after this function, the + * EEPROM will most likely contain an invalid checksum. + **/ +static s32 rnpm_write_eeprom_buffer_bit_bang(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_write_eeprom_generic - Writes 16 bit value to EEPROM + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be written to + * @data: 16 bit word to be written to the EEPROM + * + * If rnpm_eeprom_update_checksum is not called after this function, the + * EEPROM will most likely contain an invalid checksum. + **/ +s32 rnpm_write_eeprom_generic(struct rnpm_hw *hw, u16 offset, u16 data) +{ + s32 status; + + if (offset >= hw->eeprom.word_size) { + status = RNPM_ERR_EEPROM; + goto out; + } + + status = rnpm_write_eeprom_buffer_bit_bang(hw, offset, 1, &data); + +out: + return status; +} + +/** + * rnpm_read_eeprom_buffer_bit_bang_generic - Read EEPROM using bit-bang + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be read + * @words: number of word(s) + * @data: read 16 bit words(s) from EEPROM + * + * Reads 16 bit word(s) from EEPROM through bit-bang method + **/ +s32 rnpm_read_eeprom_buffer_bit_bang_generic(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_read_eeprom_buffer_bit_bang - Read EEPROM using bit-bang + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be read + * @words: number of word(s) + * @data: read 16 bit word(s) from EEPROM + * + * Reads 16 bit word(s) from EEPROM through bit-bang method + **/ +static s32 rnpm_read_eeprom_buffer_bit_bang(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_read_eeprom_bit_bang_generic - Read EEPROM word using bit-bang + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be read + * @data: read 16 bit value from EEPROM + * + * Reads 16 bit value from EEPROM through bit-bang method + **/ +s32 rnpm_read_eeprom_bit_bang_generic(struct rnpm_hw *hw, u16 offset, u16 *data) +{ + s32 status; + + if (offset >= hw->eeprom.word_size) { + status = RNPM_ERR_EEPROM; + goto out; + } + + status = rnpm_read_eeprom_buffer_bit_bang(hw, offset, 1, data); + +out: + return status; +} + +/** + * rnpm_read_eerd_buffer_generic - Read EEPROM word(s) using EERD + * @hw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @words: number of word(s) + * @data: 16 bit word(s) from the EEPROM + * + * Reads a 16 bit word(s) from the EEPROM using the EERD register. + **/ +s32 rnpm_read_eerd_buffer_generic(struct rnpm_hw *hw, u16 offset, u16 words, + u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_detect_eeprom_page_size_generic - Detect EEPROM page size + * @hw: pointer to hardware structure + * @offset: offset within the EEPROM to be used as a scratch pad + * + * Discover EEPROM page size by writing marching data at given offset. + * This function is called only when we are writing a new large buffer + * at given offset so the data would be overwritten anyway. + **/ +__maybe_unused static s32 +rnpm_detect_eeprom_page_size_generic(struct rnpm_hw *hw, u16 offset) +{ + return -EINVAL; +} + +/** + * rnpm_read_eerd_generic - Read EEPROM word using EERD + * @hw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the EERD register. + **/ +s32 rnpm_read_eerd_generic(struct rnpm_hw *hw, u16 offset, u16 *data) +{ + return rnpm_read_eerd_buffer_generic(hw, offset, 1, data); +} + +/** + * rnpm_write_eewr_buffer_generic - Write EEPROM word(s) using EEWR + * @hw: pointer to hardware structure + * @offset: offset of word in the EEPROM to write + * @words: number of words + * @data: word(s) write to the EEPROM + * + * Write a 16 bit word(s) to the EEPROM using the EEWR register. + **/ +s32 rnpm_write_eewr_buffer_generic(struct rnpm_hw *hw, u16 offset, u16 words, + u16 *data) +{ + return -EINVAL; +} + +/** + * rnpm_write_eewr_generic - Write EEPROM word using EEWR + * @hw: pointer to hardware structure + * @offset: offset of word in the EEPROM to write + * @data: word write to the EEPROM + * + * Write a 16 bit word to the EEPROM using the EEWR register. + **/ +__maybe_unused s32 rnpm_write_eewr_generic(struct rnpm_hw *hw, u16 offset, + u16 data) +{ + return rnpm_write_eewr_buffer_generic(hw, offset, 1, &data); +} + +/** + * rnpm_poll_eerd_eewr_done - Poll EERD read or EEWR write status + * @hw: pointer to hardware structure + * @ee_reg: EEPROM flag for polling + * + * Polls the status bit (bit 1) of the EERD or EEWR to determine when the + * read or write is done respectively. + **/ +__maybe_unused static s32 rnpm_poll_eerd_eewr_done(struct rnpm_hw *hw, + u32 ee_reg) +{ + return -EINVAL; +} + +/** + * rnpm_acquire_eeprom - Acquire EEPROM using bit-bang + * @hw: pointer to hardware structure + * + * Prepares EEPROM for access using bit-bang method. This function should + * be called before issuing a command to the EEPROM. + **/ +__maybe_unused static s32 rnpm_acquire_eeprom(struct rnpm_hw *hw) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_get_eeprom_semaphore - Get hardware semaphore + * @hw: pointer to hardware structure + * + * Sets the hardware semaphores so EEPROM access can occur for bit-bang method + **/ +__maybe_unused static s32 rnpm_get_eeprom_semaphore(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_release_eeprom_semaphore - Release hardware semaphore + * @hw: pointer to hardware structure + * + * This function clears hardware semaphore bits. + **/ +__maybe_unused static void rnpm_release_eeprom_semaphore(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_ready_eeprom - Polls for EEPROM ready + * @hw: pointer to hardware structure + **/ +__maybe_unused static s32 rnpm_ready_eeprom(struct rnpm_hw *hw) +{ + return -EINVAL; +} + +/** + * rnpm_standby_eeprom - Returns EEPROM to a "standby" state + * @hw: pointer to hardware structure + **/ +__maybe_unused static void rnpm_standby_eeprom(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_shift_out_eeprom_bits - Shift data bits out to the EEPROM. + * @hw: pointer to hardware structure + * @data: data to send to the EEPROM + * @count: number of bits to shift out + **/ +__maybe_unused static void rnpm_shift_out_eeprom_bits(struct rnpm_hw *hw, + u16 data, u16 count) +{ +} + +/** + * rnpm_shift_in_eeprom_bits - Shift data bits in from the EEPROM + * @hw: pointer to hardware structure + **/ +__maybe_unused static u16 rnpm_shift_in_eeprom_bits(struct rnpm_hw *hw, + u16 count) +{ + // u32 eec; + // u32 i; + // u16 data = 0; + + return 0; +} + +/** + * rnpm_raise_eeprom_clk - Raises the EEPROM's clock input. + * @hw: pointer to hardware structure + * @eec: EEC register's current value + **/ +__maybe_unused static void rnpm_raise_eeprom_clk(struct rnpm_hw *hw, u32 *eec) +{ +} + +/** + * rnpm_lower_eeprom_clk - Lowers the EEPROM's clock input. + * @hw: pointer to hardware structure + * @eecd: EECD's current value + **/ +__maybe_unused static void rnpm_lower_eeprom_clk(struct rnpm_hw *hw, u32 *eec) +{ +} + +/** + * rnpm_release_eeprom - Release EEPROM, release semaphores + * @hw: pointer to hardware structure + **/ +__maybe_unused static void rnpm_release_eeprom(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_calc_eeprom_checksum_generic - Calculates and returns the checksum + * @hw: pointer to hardware structure + **/ +__maybe_unused u16 rnpm_calc_eeprom_checksum_generic(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_validate_eeprom_checksum_generic - Validate EEPROM checksum + * @hw: pointer to hardware structure + * @checksum_val: calculated checksum + * + * Performs checksum calculation and validates the EEPROM checksum. If the + * caller does not need checksum_val, the value can be NULL. + **/ +s32 rnpm_validate_eeprom_checksum_generic(struct rnpm_hw *hw, u16 *checksum_val) +{ + return 0; +} + +/** + * rnpm_update_eeprom_checksum_generic - Updates the EEPROM checksum + * @hw: pointer to hardware structure + **/ +s32 rnpm_update_eeprom_checksum_generic(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_set_rar_generic - Set Rx address register + * @hw: pointer to hardware structure + * @index: Receive address register to write + * @addr: Address to put into receive address register + * @vmdq: VMDq "set" or "pool" index + * @enable_addr: set flag that address is active + * + * Puts an ethernet address into a receive address register. + **/ +s32 rnpm_set_rar_generic(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq, + u32 enable_addr) +{ + u32 mcstctrl; + u32 rar_low, rar_high = 0; + u32 rar_entries = hw->mac.num_rar_entries; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + + // dump_stack(); + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries + hw->ncsi_rar_entries) { + rnpm_err("set_rar_generic RAR index %d is out of range.\n", + index); + return RNPM_ERR_INVALID_ARGUMENT; + } + hw_dbg(hw, " RAR[%d] <= %pM. vmdq:%d enable:0x%x\n", index, addr, + vmdq, enable_addr); + + /* setup VMDq pool selection before this RAR gets enabled */ + /* only sriov mode use this */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + hw->mac.ops.set_vmdq(hw, index, vmdq); + + /* HW expects these in big endian so we reverse the byte + * order from network order (big endian) to little endian + */ + rar_low = ((u32)addr[5] | ((u32)addr[4] << 8) | ((u32)addr[3] << 16) | + ((u32)addr[2] << 24)); + /* Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + rar_high = rd32(hw, RNPM_ETH_RAR_RH(index)); + rar_high &= ~(0x0000FFFF | RNPM_RAH_AV); + rar_high |= ((u32)addr[1] | ((u32)addr[0] << 8)); + + if (enable_addr != 0) + rar_high |= RNPM_RAH_AV; + + wr32(hw, RNPM_ETH_RAR_RL(index), rar_low); + wr32(hw, RNPM_ETH_RAR_RH(index), rar_high); + + /* open unicast filter */ + /* we now not use unicast */ + /* but we must open this since dest-mac filter | unicast table */ + /* all packets up if close unicast table */ + mcstctrl = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + mcstctrl |= RNPM_MCSTCTRL_UNICASE_TBL_EN; + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, mcstctrl); + return 0; +} + +/* setup unicast table */ +s32 rnpm_set_rar_mac(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq, + u32 port) +{ + u32 mcstctrl; + u32 rar_low, rar_high = 0; + u32 rar_entries = hw->mac.num_rar_entries; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + + // dump_stack(); + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries + hw->ncsi_rar_entries) { + rnpm_err("set_rar_mac RAR index %d is out of range.\n", index); + return RNPM_ERR_INVALID_ARGUMENT; + } + hw_dbg(hw, "port %d RAR[%d] <= %pM. vmdq:%d\n", port, index, addr, + vmdq); + + /* setup VMDq pool selection before this RAR gets enabled */ + /* only sriov mode use this */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + // fixme + hw->mac.ops.set_vmdq(hw, index, vmdq); + + /* HW expects these in big endian so we reverse the byte + * order from network order (big endian) to little endian + */ + rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) | ((u32)addr[2] << 16) | + ((u32)addr[3] << 24)); + + rar_high = RNPM_RAH_AV | ((u32)addr[4] | (u32)addr[5] << 8); + + wr32(hw, RNPM_MAC_UNICAST_HIGH(index, port), rar_high); + wr32(hw, RNPM_MAC_UNICAST_LOW(index, port), rar_low); + + /* use unicast perfect match */ + mcstctrl = rd32(hw, RNPM_MAC_PKT_FLT(port)); + mcstctrl &= (~RNPM_RX_HUC); + wr32(hw, RNPM_MAC_PKT_FLT(port), mcstctrl); + return 0; +} +/** + * rnpm_clear_rar_generic - Remove Rx address register + * @hw: pointer to hardware structure + * @index: Receive address register to write + * + * Clears an ethernet address from a receive address register. + **/ +s32 rnpm_clear_rar_generic(struct rnpm_hw *hw, u32 index) +{ + u32 rar_high; + u32 rar_entries = hw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries + hw->ncsi_rar_entries) { + hw_dbg(hw, "clear_rar_generic RAR index %d is out of range.\n", + index); + return RNPM_ERR_INVALID_ARGUMENT; + } + + /* Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + rar_high = rd32(hw, RNPM_ETH_RAR_RH(index)); + rar_high &= ~(0x0000FFFF | RNPM_RAH_AV); + + // hw_dbg(hw, "Clearing RAR[%d]\n", index); + wr32(hw, RNPM_ETH_RAR_RL(index), 0); + wr32(hw, RNPM_ETH_RAR_RH(index), rar_high); + + /* clear VMDq pool/queue selection for this RAR */ + hw->mac.ops.clear_vmdq(hw, index, RNPM_CLEAR_VMDQ_ALL); + + return 0; +} + +s32 rnpm_clear_rar_mac(struct rnpm_hw *hw, u32 index, u32 port) +{ + // u32 rar_high; + u32 rar_entries = hw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries + hw->ncsi_rar_entries) { + hw_dbg(hw, "clear_rar_mac RAR index %d is out of range.\n", + index); + return RNPM_ERR_INVALID_ARGUMENT; + } + + /* Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + wr32(hw, RNPM_MAC_UNICAST_LOW(index, port), 0); + wr32(hw, RNPM_MAC_UNICAST_HIGH(index, port), 0); + + /* clear VMDq pool/queue selection for this RAR */ + // hw->mac.ops.clear_vmdq(hw, index, RNPM_CLEAR_VMDQ_ALL); + + return 0; +} + +/** + * rnpm_set_mta - Set bit-vector in multicast table + * @hw: pointer to hardware structure + * @hash_value: Multicast address hash value + * + * Sets the bit-vector in the multicast table. + **/ +static void rnpm_set_mta(struct rnpm_hw *hw, u8 *mc_addr) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u8 port = adapter->port; + u32 vector; + u32 vector_bit; + u32 vector_reg; + + /* if use mc hash table in mac */ + /* don't update pf mta table */ + if (hw->mac.mc_location == rnpm_mc_location_nic) + pf_adapter->mta_in_use[port]++; + hw->addr_ctrl.mta_in_use++; + vector = rnpm_mta_vector(hw->mac.mc_filter_type, mc_addr); + + /* low 5 bits indicate bit pos + * high 3 (mac) or 7 (nic) bits indicate reg pos + */ + vector_reg = (vector >> 5) & 0x7F; + vector_bit = vector & 0x1F; + hw_dbg(hw, "\t\t%pM: MTA-BIT:%4d, MTA_REG[%d][%d] <= 1\n", mc_addr, + vector, vector_reg, vector_bit); + if (hw->mac.mc_location == rnpm_mc_location_nic) + pf_adapter->mta_shadow[vector_reg] |= (1 << vector_bit); + hw->mac.mta_shadow[vector_reg] |= (1 << vector_bit); +} + +static int __get_ncsi_shm_info(struct rnpm_hw *hw, + struct ncsi_shm_info *ncsi_shm) +{ + int i; + int *ptr = (int *)ncsi_shm; + int rbytes = round_up(sizeof(*ncsi_shm), 4); + + memset(ncsi_shm, 0, sizeof(*ncsi_shm)); + for (i = 0; i < (rbytes / 4); i++) + ptr[i] = rd32(hw, hw->ncsi_vf_cpu_shm_pf_base + 4 * i); + + return (ncsi_shm->valid & RNPM_NCSI_SHM_VALID_MASK) == + RNPM_NCSI_SHM_VALID; +} + +void rnpm_ncsi_set_uc_addr_generic(struct rnpm_hw *hw) +{ + struct ncsi_shm_info ncsi_shm; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 mac[ETH_ALEN]; + + if (hw->ncsi_en) { + if (__get_ncsi_shm_info(hw, &ncsi_shm)) { + if (ncsi_shm.valid & RNPM_MC_VALID) { + mac[0] = ncsi_shm.uc.uc_addr_lo & 0xff; + mac[1] = (ncsi_shm.uc.uc_addr_lo >> 8) & 0xff; + mac[2] = (ncsi_shm.uc.uc_addr_lo >> 16) & 0xff; + mac[3] = (ncsi_shm.uc.uc_addr_lo >> 24) & 0xff; + mac[4] = ncsi_shm.uc.uc_addr_hi & 0xff; + mac[5] = (ncsi_shm.uc.uc_addr_hi >> 8) & 0xff; + if (is_valid_ether_addr(mac)) { + // WARN_ON(1); + hw->mac.ops.set_rar( + hw, hw->mac.num_rar_entries, + mac, VMDQ_P(0), RNPM_RAH_AV); + if (hw->mac.mc_location == + rnpm_mc_location_mac) { + hw->mac.ops.set_rar_mac( + hw, 31, mac, VMDQ_P(0), + adapter->port); + } + } + } + } + } +} + +void rnpm_ncsi_set_mc_mta_generic(struct rnpm_hw *hw) +{ + struct ncsi_shm_info ncsi_shm; + u8 i; + u8 mac[ETH_ALEN]; + + if (hw->ncsi_en) { + if (__get_ncsi_shm_info(hw, &ncsi_shm)) { + if (ncsi_shm.valid & RNPM_MC_VALID) { + for (i = 0; i < RNPM_NCSI_MC_COUNT; i++) { + mac[0] = ncsi_shm.mc[i].mc_addr_lo & + 0xff; + mac[1] = (ncsi_shm.mc[i].mc_addr_lo >> + 8) & + 0xff; + mac[2] = (ncsi_shm.mc[i].mc_addr_lo >> + 16) & + 0xff; + mac[3] = (ncsi_shm.mc[i].mc_addr_lo >> + 24) & + 0xff; + mac[4] = ncsi_shm.mc[i].mc_addr_hi & + 0xff; + mac[5] = (ncsi_shm.mc[i].mc_addr_hi >> + 8) & + 0xff; + // ncsi_shm.mc[i].mc_addr_hi); + if (is_multicast_ether_addr(mac) && + !is_zero_ether_addr(mac)) { + rnpm_set_mta(hw, mac); + } + } + } + } + } +} + +void rnpm_ncsi_set_vfta_mac_generic(struct rnpm_hw *hw) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct ncsi_shm_info ncsi_shm; + + if (hw->ncsi_en) { + if (__get_ncsi_shm_info(hw, &ncsi_shm)) { + if (ncsi_shm.valid & RNPM_VLAN_VALID) { + hw->mac.ops.set_vfta_mac(hw, ncsi_shm.ncsi_vlan, + VMDQ_P(0), true); + } + } + } +} + +/** + * rnpm_init_rx_addrs_generic - Initializes receive address filters. + * @hw: pointer to hardware structure + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive address registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + **/ +s32 rnpm_init_rx_addrs_generic(struct rnpm_hw *hw) +{ + u32 i; + // u32 rar_entries = hw->mac.num_rar_entries; + u32 v; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u32 rar_entries = adapter->uc_num; + u8 port = adapter->port; + + hw_dbg(hw, "init_rx_addrs:rar_entries:%d, mac.addr:%pM\n", rar_entries, + hw->mac.addr); + /* If the current mac address is valid, assume it is a software override + * to the permanent address. + * Otherwise, use the permanent address from the eeprom. + */ + if (!is_valid_ether_addr(hw->mac.addr)) { + /* Get the MAC address from the RAR0 for later reference */ + hw->mac.ops.get_mac_addr(hw, hw->mac.addr); + hw_dbg(hw, " Keeping Current RAR0 Addr =%pM\n", hw->mac.addr); + } else { + /* Setup the receive address. */ + hw_dbg(hw, "Overriding MAC Address in RAR[0]\n"); + hw_dbg(hw, " New MAC Addr =%pM\n", hw->mac.addr); + + hw->mac.ops.set_rar(hw, adapter->uc_off, hw->mac.addr, 0, + RNPM_RAH_AV); + + /* clear VMDq pool/queue selection for RAR 0 */ + hw->mac.ops.clear_vmdq(hw, 0, RNPM_CLEAR_VMDQ_ALL); + } + hw->addr_ctrl.overflow_promisc = 0; + + hw->addr_ctrl.rar_used_count = 1; + + /* Zero out the other receive addresses. */ + hw_dbg(hw, "Clearing RAR[%d-%d]\n", adapter->uc_off + 1, + rar_entries + adapter->uc_off - 1); + for (i = adapter->uc_off + 1; i < rar_entries; i++) { + wr32(hw, RNPM_ETH_RAR_RL(i), 0); + wr32(hw, RNPM_ETH_RAR_RH(i), 0); + } + + if (hw->mac.mc_location == rnpm_mc_location_mac) { + for (i = 1; i < adapter->uc_num; i++) { + wr32(hw, RNPM_MAC_UNICAST_HIGH(i, port), 0); + wr32(hw, RNPM_MAC_UNICAST_LOW(i, port), 0); + } + } + + /* Clear the MTA */ + hw->addr_ctrl.mta_in_use = 0; + + if (hw->mac.mc_location == rnpm_mc_location_nic) { + v = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + v &= (~(RNPM_MCSTCTRL_MULTICASE_TBL_EN | + RNPM_MCSTCTRL_UNICASE_TBL_EN)); + v |= hw->mac.mc_filter_type; + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, v); + hw_dbg(hw, " Clearing MTA\n"); + for (i = 0; i < hw->mac.mcft_size; i++) + wr32(hw, RNPM_MTA(i), 0); + } else { + v = rd32(hw, RNPM_MAC_PKT_FLT(port)); + v &= (~RNPM_FLT_HUC); + wr32(hw, RNPM_MAC_PKT_FLT(port), v); + hw_dbg(hw, " Clearing MTA\n"); + for (i = 0; i < hw->mac.mcft_size; i++) + wr32(hw, RNPM_MAC_MC_HASH_TABLE(port, i), 0); + if (hw->ncsi_en) { + rnpm_ncsi_set_mc_mta_generic(hw); + for (i = 0; i < hw->mac.mcft_size; i++) { + wr32(hw, RNPM_MAC_MC_HASH_TABLE(port, i), + hw->mac.mta_shadow[i]); + } + /* Set ncsi vlan */ + rnpm_ncsi_set_vfta_mac_generic(hw); + } + } + + if (hw->mac.ops.init_uta_tables) + hw->mac.ops.init_uta_tables(hw); + + if (hw->ncsi_en) + rnpm_ncsi_set_uc_addr_generic(hw); + + return 0; +} +static u32 rnpm_calc_crc32(u32 seed, u8 *mac, u32 len) +{ +#define RNPM_CRC32_POLY_LE 0xedb88320 + u32 crc = seed; + u32 i; + + while (len--) { + crc ^= *mac++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? RNPM_CRC32_POLY_LE : 0); + } + + return crc; +} + +/** + * rnpm_mta_vector - Determines bit-vector in multicast table to set + * @hw: pointer to hardware structure + * @mc_addr: the multicast address + * + * Extracts the 12 bits, from a multicast address, to determine which + * bit-vector to set in the multicast table. The hardware uses 12 bits, from + * incoming rx multicast addresses, to determine the bit-vector to check in + * the MTA. Which of the 4 combination, of 12-bits, the hardware uses is set + * by the MO field of the MCSTCTRL. The MO field is set during initialization + * to mc_filter_type. + **/ +static s32 rnpm_mta_vector(int mode, u8 *mc_addr) +{ + u32 vector = 0; + + switch (mode) { + case 0: /* use bits [36:47] of the address */ + vector = ((mc_addr[4] << 8) | (((u16)mc_addr[5]))); + break; + case 1: /* use bits [35:46] of the address */ + vector = ((mc_addr[4] << 7) | (((u16)mc_addr[5]) >> 1)); + break; + case 2: /* use bits [34:45] of the address */ + vector = ((mc_addr[4] << 6) | (((u16)mc_addr[5]) >> 2)); + break; + case 3: /* use bits [32:43] of the address */ + vector = ((mc_addr[4] << 5) | (((u16)mc_addr[5]) >> 3)); + break; + case 4: + /* hash is used for multicast address */ + /* only high 8 bits used */ +#define DEFAULT_MAC_LEN (6) + vector = bitrev32( + ~rnpm_calc_crc32(~0, mc_addr, DEFAULT_MAC_LEN)); + vector = vector >> 24; + break; + default: /* Invalid mc_filter_type */ + hw_dbg(hw, "MC filter type param set incorrectly\n"); + break; + } + + /* vector can only be 12-bits or boundary will be exceeded */ + vector &= 0xFFF; + return vector; +} + +static u8 *rnpm_addr_list_itr(struct rnpm_hw __maybe_unused *hw, + u8 **mc_addr_ptr) +{ +#ifdef NETDEV_HW_ADDR_T_MULTICAST + struct netdev_hw_addr *mc_ptr; +#else + struct dev_mc_list *mc_ptr; +#endif + u8 *addr = *mc_addr_ptr; + +#ifdef NETDEV_HW_ADDR_T_MULTICAST + mc_ptr = container_of(addr, struct netdev_hw_addr, addr[0]); + if (mc_ptr->list.next) { + struct netdev_hw_addr *ha; + + ha = list_entry(mc_ptr->list.next, struct netdev_hw_addr, list); + *mc_addr_ptr = ha->addr; + } +#else + mc_ptr = container_of(addr, struct dev_mc_list, dmi_addr[0]); + if (mc_ptr->next) + *mc_addr_ptr = mc_ptr->next->dmi_addr; +#endif + else + *mc_addr_ptr = NULL; + + return addr; +} + +/** + * rnpm_update_mc_addr_list_generic - Updates MAC list of multicast addresses + * @hw: pointer to hardware structure + * @netdev: pointer to net device structure + * + * The given list replaces any existing list. Clears the MC addrs from receive + * address registers and the multicast table. Uses unused receive address + * registers for the first multicast addresses, and hashes the rest into the + * multicast table. + **/ +s32 rnpm_update_mutiport_mc_addr_list_generic(struct rnpm_hw *hw, + struct net_device *netdev) +{ +#ifdef NETDEV_HW_ADDR_T_MULTICAST + struct netdev_hw_addr *ha; +#endif + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u32 i; + u32 v; + u8 port = adapter->port; + int addr_count = 0; + u8 *addr_list = NULL; + unsigned long flags; + + /* Set the new number of MC addresses that we are being requested to + * use. + */ + pf_adapter->num_mc_addrs[port] = netdev_mc_count(netdev); + pf_adapter->mta_in_use[port] = 0; + + hw->addr_ctrl.num_mc_addrs = netdev_mc_count(netdev); + hw->addr_ctrl.mta_in_use = 0; + + /* Clear share mta_shadow only not in mutiport mode */ + if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) { + hw_dbg(hw, " Clearing MTA(multicast table)\n"); + memset(&pf_adapter->mta_shadow, 0, + sizeof(pf_adapter->mta_shadow)); + } + /* clear own mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + spin_lock_irqsave(&pf_adapter->mc_setup_lock, flags); + /* Update mta shadow */ + hw_dbg(hw, "port %d Updating MTA..\n", port); + // netdev_for_each_mc_addr(ha, netdev) { + // rnpm_set_mta(hw, ha->addr); + // } + addr_count = netdev_mc_count(netdev); + +#ifdef NETDEV_HW_ADDR_T_MULTICAST + ha = list_first_entry(&netdev->mc.list, struct netdev_hw_addr, list); + addr_list = ha->addr; +#else + addr_list = netdev->mc_list->dmi_addr; +#endif + + for (i = 0; i < addr_count; i++) { + hw_dbg(hw, " Adding the multicast addresses:\n"); + rnpm_set_mta(hw, rnpm_addr_list_itr(hw, &addr_list)); + } + /* unicast and multicast use the same hash table */ + if (hw->ncsi_en) + rnpm_ncsi_set_mc_mta_generic(hw); + + /* update mta table to the corect location */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + for (i = 0; i < hw->mac.mcft_size; i++) { + if (hw->addr_ctrl.mta_in_use) { + wr32(hw, RNPM_MAC_MC_HASH_TABLE(port, i), + hw->mac.mta_shadow[i]); + } + } + } else { + for (i = 0; i < pf_adapter->mcft_size; i++) { + if (pf_adapter->mta_in_use[port]) { + wr32(hw, RNPM_ETH_MUTICAST_HASH_TABLE(i), + pf_adapter->mta_shadow[i]); + } + } + } + spin_unlock_irqrestore(&pf_adapter->mc_setup_lock, flags); + + if (hw->mac.mc_location == rnpm_mc_location_nic) { + if (pf_adapter->mta_in_use[port] > 0) { + v = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, + v | RNPM_MCSTCTRL_MULTICASE_TBL_EN | + pf_adapter->mc_filter_type); + } + /* setup delay update mta */ + /* check this port mta equal pf_adapter? */ + adapter->flags_feature |= RNPM_FLAG_DELAY_UPDATE_MUTICAST_TABLE; + hw_dbg(hw, "nic mode update MTA Done. mta_in_use:%d\n", + pf_adapter->mta_in_use[port]); + } else { + if (hw->addr_ctrl.mta_in_use) { + v = rd32(hw, RNPM_MAC_PKT_FLT(port)); + v |= RNPM_FLT_HMC; + wr32(hw, RNPM_MAC_PKT_FLT(port), v); + } + + hw_dbg(hw, "mac mode update MTA Done. mta_in_use:%d\n", + hw->addr_ctrl.mta_in_use); + } + if (hw->mac.mc_location == rnpm_mc_location_nic) + return pf_adapter->mta_in_use[port]; + else + return hw->addr_ctrl.mta_in_use; +} + +/* rnpm_update_mc_addr_list_generic - Updates MAC list of multicast addresses + * @hw: pointer to hardware structure + * @netdev: pointer to net device structure + * + * The given list replaces any existing list. Clears the MC addrs from receive + * address registers and the multicast table. Uses unused receive address + * registers for the first multicast addresses, and hashes the rest into the + * multicast table. + */ +s32 rnpm_update_mc_addr_list_generic(struct rnpm_hw *hw, + struct net_device *netdev) +{ +#ifdef NETDEV_HW_ADDR_T_MULTICAST + struct netdev_hw_addr *ha; +#endif + u32 i; + u32 v; + int addr_count = 0; + u8 *addr_list = NULL; + + /* Set the new number of MC addresses that we are being requested to + * use. + */ + hw->addr_ctrl.num_mc_addrs = netdev_mc_count(netdev); + hw->addr_ctrl.mta_in_use = 0; + + /* Clear mta_shadow */ + hw_dbg(hw, " Clearing MTA(multicast table)\n"); + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* Update mta shadow */ + hw_dbg(hw, " Updating MTA..\n"); + addr_count = netdev_mc_count(netdev); + +#ifdef NETDEV_HW_ADDR_T_MULTICAST + ha = list_first_entry(&netdev->mc.list, struct netdev_hw_addr, list); + addr_list = ha->addr; +#else + addr_list = netdev->mc_list->dmi_addr; +#endif + // netdev_for_each_mc_addr(ha, netdev) { + // rnpm_set_mta(hw, ha->addr); + // } + + for (i = 0; i < addr_count; i++) { + hw_dbg(hw, " Adding the multicast addresses:\n"); + rnpm_set_mta(hw, rnpm_addr_list_itr(hw, &addr_list)); + } + + /* Enable mta */ + for (i = 0; i < hw->mac.mcft_size; i++) { + if (hw->addr_ctrl.mta_in_use) { + wr32(hw, RNPM_ETH_MUTICAST_HASH_TABLE(i), + hw->mac.mta_shadow[i]); + } + } + + if (hw->addr_ctrl.mta_in_use > 0) { + v = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, + v | RNPM_MCSTCTRL_MULTICASE_TBL_EN | + hw->mac.mc_filter_type); + } + + hw_dbg(hw, " update MTA Done. mta_in_use:%d\n", + hw->addr_ctrl.mta_in_use); + return hw->addr_ctrl.mta_in_use; +} + +/** + * rnpm_enable_mc_generic - Enable multicast address in RAR + * @hw: pointer to hardware structure + * + * Enables multicast address in RAR and the use of the multicast hash table. + **/ +s32 rnpm_enable_mc_generic(struct rnpm_hw *hw) +{ + struct rnpm_addr_filter_info *a = &hw->addr_ctrl; + u32 v; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 port = adapter->port; + + if (a->mta_in_use > 0) { + if (hw->mac.mc_location == rnpm_mc_location_nic) { + v = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + v |= RNPM_MCSTCTRL_MULTICASE_TBL_EN; + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, v); + } else { + v = rd32(hw, RNPM_MAC_PKT_FLT(port)); + v |= RNPM_FLT_HMC; + wr32(hw, RNPM_MAC_PKT_FLT(port), v); + } + } + + return 0; +} + +/** + * rnpm_disable_mc_generic - Disable multicast address in RAR + * @hw: pointer to hardware structure + * + * Disables multicast address in RAR and the use of the multicast hash table. + **/ +s32 rnpm_disable_mc_generic(struct rnpm_hw *hw) +{ + struct rnpm_addr_filter_info *a = &hw->addr_ctrl; + u32 v; + + if (a->mta_in_use > 0) { + v = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + v &= ~RNPM_MCSTCTRL_MULTICASE_TBL_EN; + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, v); + } + + return 0; +} + +/* rnpm_fc_enable_generic - Enable flow control + * @hw: pointer to hardware structure + * + * Enable flow control according to the current settings. + */ +s32 rnpm_fc_enable_generic(struct rnpm_hw *hw) +{ + s32 ret_val = 0; + u32 reg; + u32 rxctl_reg, txctl_reg[RNPM_MAX_TRAFFIC_CLASS]; + int i; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 port = adapter->port; + + hw->fc.current_mode = hw->fc.requested_mode; + /* Validate the water mark configuration for packet buffer 0. Zero + * water marks indicate that the packet buffer was not configured + * and the watermarks for packet buffer 0 should always be configured. + */ + if (!hw->fc.pause_time) { + ret_val = RNPM_ERR_INVALID_LINK_SETTINGS; + goto out; + } + + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & rnpm_fc_tx_pause) && + hw->fc.high_water[i]) { + if (!hw->fc.low_water[i] || + hw->fc.low_water[i] >= hw->fc.high_water[i]) { + hw_dbg(hw, + "Invalid water mark configuration\n"); + ret_val = RNPM_ERR_INVALID_LINK_SETTINGS; + goto out; + } + } + } + + /* Negotiate the fc mode to use */ + rnpm_fc_autoneg(hw); + + /* Disable any previous flow control settings */ + rxctl_reg = rd32(hw, RNPM_MAC_RX_FLOW_CTRL(port)); + rxctl_reg &= (~RNPM_RX_FLOW_ENABLE_MASK); + + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) { + txctl_reg[i] = rd32(hw, RNPM_MAC_Q0_TX_FLOW_CTRL(port, i)); + txctl_reg[i] &= (~RNPM_TX_FLOW_ENABLE_MASK); + } + /** + * The possible values of fc.current_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.current_mode) { + case rnpm_fc_none: + /** + * Flow control is disabled by software override or + * autoneg.The code below will actually disable it + * in the HW. + */ + break; + case rnpm_fc_rx_pause: + /** + * Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + rxctl_reg |= (RNPM_RX_FLOW_ENABLE_MASK); + break; + case rnpm_fc_tx_pause: + /** + * Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) + txctl_reg[i] |= (RNPM_TX_FLOW_ENABLE_MASK); + break; + case rnpm_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + rxctl_reg |= (RNPM_RX_FLOW_ENABLE_MASK); + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) + txctl_reg[i] |= (RNPM_TX_FLOW_ENABLE_MASK); + break; + default: + hw_dbg(hw, "Flow control mac param set incorrectly\n"); + ret_val = RNPM_ERR_CONFIG; + goto out; + } + /* Set up and enable Rx high/low water mark thresholds, enable XON. */ + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & rnpm_fc_tx_pause)) { + if (hw->fc.high_water[i]) + wr32(hw, RNPM_ETH_HIGH_WATER(i), + hw->fc.high_water[i]); + if (hw->fc.low_water[i]) + wr32(hw, RNPM_ETH_LOW_WATER(i), + hw->fc.low_water[i]); + } + } + + /* Configure pause time (2 TCs per register) */ + reg = hw->fc.pause_time; + for (i = 0; i < (RNPM_MAX_TRAFFIC_CLASS); i++) + txctl_reg[i] |= (reg << 16); + + /* Set 802.3x based flow control settings. */ + wr32(hw, RNPM_MAC_RX_FLOW_CTRL(port), rxctl_reg); + for (i = 0; i < (RNPM_MAX_TRAFFIC_CLASS); i++) + wr32(hw, RNPM_MAC_Q0_TX_FLOW_CTRL(port, i), txctl_reg[i]); + // rnpm_setup_fc(hw); +out: + return ret_val; +} + +/** + * rnpm_negotiate_fc - Negotiate flow control + * @hw: pointer to hardware structure + * @adv_reg: flow control advertised settings + * @lp_reg: link partner's flow control settings + * @adv_sym: symmetric pause bit in advertisement + * @adv_asm: asymmetric pause bit in advertisement + * @lp_sym: symmetric pause bit in link partner advertisement + * @lp_asm: asymmetric pause bit in link partner advertisement + * + * Find the intersection between advertised settings and link partner's + * advertised settings + **/ +__maybe_unused static s32 rnpm_negotiate_fc(struct rnpm_hw *hw, u32 adv_reg, + u32 lp_reg, u32 adv_sym, + u32 adv_asm, u32 lp_sym, u32 lp_asm) +{ + if ((!(adv_reg)) || (!(lp_reg))) + return RNPM_ERR_FC_NOT_NEGOTIATED; + + if ((adv_reg & adv_sym) && (lp_reg & lp_sym)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == rnpm_fc_full) { + hw->fc.current_mode = rnpm_fc_full; + hw_dbg(hw, "Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = rnpm_fc_rx_pause; + hw_dbg(hw, "Flow Control=RX PAUSE frames only\n"); + } + } else if (!(adv_reg & adv_sym) && (adv_reg & adv_asm) && + (lp_reg & lp_sym) && (lp_reg & lp_asm)) { + hw->fc.current_mode = rnpm_fc_tx_pause; + hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n"); + } else if ((adv_reg & adv_sym) && (adv_reg & adv_asm) && + !(lp_reg & lp_sym) && (lp_reg & lp_asm)) { + hw->fc.current_mode = rnpm_fc_rx_pause; + hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n"); + } else { + hw->fc.current_mode = rnpm_fc_none; + hw_dbg(hw, "Flow Control = NONE.\n"); + } + return 0; +} + +/** + * rnpm_fc_autoneg_fiber - Enable flow control on 1 gig fiber + * @hw: pointer to hardware structure + * + * Enable flow control according on 1 gig fiber. + **/ +__maybe_unused static s32 rnpm_fc_autoneg_fiber(struct rnpm_hw *hw) +{ + s32 ret_val = RNPM_ERR_FC_NOT_NEGOTIATED; + + return ret_val; +} + +/** + * rnpm_fc_autoneg_backplane - Enable flow control IEEE clause 37 + * @hw: pointer to hardware structure + * + * Enable flow control according to IEEE clause 37. + **/ +__maybe_unused static s32 rnpm_fc_autoneg_backplane(struct rnpm_hw *hw) +{ + s32 ret_val = RNPM_ERR_FC_NOT_NEGOTIATED; + + return ret_val; +} + +/* rnpm_fc_autoneg_copper - Enable flow control IEEE clause 37 + * @hw: pointer to hardware structure + * + * Enable flow control according to IEEE clause 37. + */ +__maybe_unused static s32 rnpm_fc_autoneg_copper(struct rnpm_hw *hw) +{ + u16 technology_ability_reg = 0; + u16 lp_technology_ability_reg = 0; + + hw->phy.ops.read_reg(hw, 4, 0, &technology_ability_reg); + hw->phy.ops.read_reg(hw, 5, 0, &lp_technology_ability_reg); + + return rnpm_negotiate_fc(hw, (u32)technology_ability_reg, + (u32)lp_technology_ability_reg, + RNPM_TAF_SYM_PAUSE, RNPM_TAF_ASM_PAUSE, + RNPM_TAF_SYM_PAUSE, RNPM_TAF_ASM_PAUSE); +} + +/** + * rnpm_fc_autoneg - Configure flow control + * @hw: pointer to hardware structure + * + * Compares our advertised flow control capabilities to those advertised by + * our link partner, and determines the proper flow control mode to use. + **/ +void rnpm_fc_autoneg(struct rnpm_hw *hw) +{ + s32 ret_val = RNPM_ERR_FC_NOT_NEGOTIATED; + // rnpm_link_speed speed; + // bool link_up; + + /* AN should have completed when the cable was plugged in. + * Look for reasons to bail out. Bail out if: + * - FC autoneg is disabled, or if + * - link is not up. + * + * Since we're being called from an LSC, link is already known to be up. + * So use link_up_wait_to_complete=false. + */ + // if (hw->fc.disable_fc_autoneg) + // goto out; + + // hw->mac.ops.check_link(hw, &speed, &link_up, false); + // if (!link_up) + // goto out; + + switch (hw->phy.media_type) { + /* Autoneg flow control on fiber adapters */ + case rnpm_media_type_fiber: + // if (speed == RNPM_LINK_SPEED_1GB_FULL) + // ret_val = rnpm_fc_autoneg_fiber(hw); + break; + + /* Autoneg flow control on backplane adapters */ + case rnpm_media_type_backplane: + // ret_val = rnpm_fc_autoneg_backplane(hw); + break; + + /* Autoneg flow control on copper adapters */ + case rnpm_media_type_copper: + if (rnpm_device_supports_autoneg_fc(hw)) + ret_val = rnpm_fc_autoneg_copper(hw); + break; + + default: + break; + } + + // out: + if (ret_val == 0) { + hw->fc.fc_was_autonegged = true; + } else { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + } +} + +/** + * rnpm_disable_pcie_master - Disable PCI-express master access + * @hw: pointer to hardware structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. RNPM_ERR_MASTER_REQUESTS_PENDING is returned if master disable + * bit hasn't caused the master requests to be disabled, else 0 + * is returned signifying master requests disabled. + **/ +static s32 rnpm_disable_pcie_master(struct rnpm_hw *hw) +{ + // struct rnpm_adapter *adapter = hw->back; + + // disable dma rx/tx + wr32(hw, RNPM_DMA_AXI_EN, 0); + + return 0; +} + +/** + * rnpm_acquire_swfw_sync - Acquire SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to acquire + * + * Acquires the SWFW semaphore through the GSSR register for the specified + * function (CSR, PHY0, PHY1, EEPROM, Flash) + **/ +s32 rnpm_acquire_swfw_sync(struct rnpm_hw *hw, u16 mask) +{ + return 0; +} + +/** + * rnpm_release_swfw_sync - Release SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to release + * + * Releases the SWFW semaphore through the GSSR register for the specified + * function (CSR, PHY0, PHY1, EEPROM, Flash) + **/ +void rnpm_release_swfw_sync(struct rnpm_hw *hw, u16 mask) +{ +} + +/** + * rnpm_disable_rx_buff_generic - Stops the receive data path + * @hw: pointer to hardware structure + * + * Stops the receive data path and waits for the HW to internally + * empty the Rx security block. + **/ +s32 rnpm_disable_rx_buff_generic(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_enable_rx_buff - Enables the receive data path + * @hw: pointer to hardware structure + * + * Enables the receive data path + **/ +s32 rnpm_enable_rx_buff_generic(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_enable_rx_dma_generic - MAC Enable the Rx DMA unit + * @hw: pointer to hardware structure + * @regval: register value to write to RXCTRL + * + * Enables the Rx DMA unit + **/ +s32 rnpm_enable_rx_dma_generic(struct rnpm_hw *hw, u32 regval) +{ + // RNPM_WRITE_REG(hw, RNPM_RXCTRL, regval); + + return 0; +} + +/** + * rnpm_blink_led_start_generic - Blink LED based on index. + * @hw: pointer to hardware structure + * @index: led number to blink + **/ +s32 rnpm_blink_led_start_generic(struct rnpm_hw *hw, u32 index) +{ + s32 ret_val = 0; + + return ret_val; +} + +/** + * rnpm_blink_led_stop_generic - Stop blinking LED based on index. + * @hw: pointer to hardware structure + * @index: led number to stop blinking + **/ +s32 rnpm_blink_led_stop_generic(struct rnpm_hw *hw, u32 index) +{ + s32 ret_val = 0; + + return ret_val; +} + +/** + * rnpm_clear_vmdq_generic - Disassociate a VMDq pool index from a rx address + * @hw: pointer to hardware struct + * @rar: receive address register index to disassociate + * @vmdq: VMDq pool index to remove from the rar + **/ +s32 rnpm_clear_vmdq_generic(struct rnpm_hw *hw, u32 rar, u32 vmdq) +{ + u32 rar_entries = hw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries + hw->ncsi_rar_entries) { + hw_dbg(hw, "clear_vmdq_generic RAR index %d is out of range.\n", + rar); + return RNPM_ERR_INVALID_ARGUMENT; + } + + wr32(hw, RNPM_VM_DMAC_MPSAR_RING(rar), 0); + + return 0; +} + +/** + * rnpm_set_vmdq_generic - Associate a VMDq pool index with a rx address + * @hw: pointer to hardware struct + * @rar: receive address register index to associate with a VMDq index + * @vmdq: VMDq pool index + **/ +s32 rnpm_set_vmdq_generic(struct rnpm_hw *hw, u32 rar, u32 vmdq) +{ + // u32 mpsar; + u32 rar_entries = hw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries + hw->ncsi_rar_entries) { + hw_dbg(hw, "set_vmdq_generic RAR index %d is out of range.\n", + rar); + return RNPM_ERR_INVALID_ARGUMENT; + } + wr32(hw, RNPM_VM_DMAC_MPSAR_RING(rar), vmdq); + return 0; +} + +/** + * rnpm_init_uta_tables_generic - Initialize the Unicast Table Array + * @hw: pointer to hardware structure + **/ +s32 rnpm_init_uta_tables_generic(struct rnpm_hw *hw) +{ + int i; + /* not so good */ + for (i = 0; i < hw->mac.num_rar_entries; i++) + wr32(hw, RNPM_ETH_UTA(i), 0); + return 0; +} + +/** + * rnpm_find_vlvf_slot - find the vlanid or the first empty slot + * @hw: pointer to hardware structure + * @vlan: VLAN id to write to VLAN filter + * + * return the VLVF index where this VLAN id should be placed + * + **/ +__maybe_unused static s32 rnpm_find_vlvf_slot(struct rnpm_hw *hw, u32 vlan) +{ + u32 bits = 0; + u32 first_empty_slot = 0; + s32 regindex = -1; + + /* short cut the special case */ + if (vlan == 0) + return 0; + + /* Search for the vlan id in the VLVF entries. Save off the first empty + * slot found along the way + */ + for (regindex = 1; regindex < RNPM_VLVF_ENTRIES; regindex++) { + bits = rd32(hw, RNPM_VLVF(regindex)); + if (!bits && !(first_empty_slot)) + first_empty_slot = regindex; + else if ((bits & 0x0FFF) == vlan) + break; + } + + /* If regindex is less than RNPM_VLVF_ENTRIES, then we found the vlan + * in the VLVF. Else use the first empty VLVF register for this + * vlan id. + */ + if (regindex >= RNPM_VLVF_ENTRIES) { + if (first_empty_slot) + regindex = first_empty_slot; + else { + hw_dbg(hw, "No space in VLVF.\n"); + regindex = RNPM_ERR_NO_SPACE; + } + } + return regindex; +} + +s32 rnpm_set_vfta_mac_generic(struct rnpm_hw *hw, u32 vlan, u32 vind, + bool vlan_on) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 port = adapter->port; + u32 value, vector; + u16 vid; + + /* todo in vf mode vlvf regester can be set according to vind*/ + if (vlan > 4095 || vlan == 0) + return RNPM_ERR_PARAM; + + value = rd32(hw, RNPM_MAC_VLAN_HASH_TB(port)); + + vid = cpu_to_le16(vlan); + vector = bitrev32(~rnpm_vid_crc32_le(vid)); + // vector = bitrev32(~rnpm_calc_crc32(~0, (u8 *)&vlan, 2)); + vector = vector >> 28; + value |= (1 << vector); + + wr32(hw, RNPM_MAC_VLAN_HASH_TB(port), value); + + return 0; +} + +/** + * rnpm_set_vfta_generic - Set VLAN filter table + * @hw: pointer to hardware structure + * @vlan: VLAN id to write to VLAN filter + * @vind: VMDq output index that maps queue to VLAN id in VFVFB + * @vlan_on: boolean flag to turn on/off VLAN in VFVF + * + * Turn on/off specified VLAN in the VLAN filter table. + **/ +s32 rnpm_set_vfta_generic(struct rnpm_hw *hw, u32 vlan, u32 vind, bool vlan_on) +{ + s32 regindex; + u32 bitindex; + u32 vfta; + u32 targetbit; + bool vfta_changed = false; + + /* todo in vf mode vlvf regester can be set according to vind*/ + if (vlan > 4095) + return RNPM_ERR_PARAM; + + /* this is a 2 part operation - first the VFTA, then the + * VLVF and VLVFB if VT Mode is set + * We don't write the VFTA until we know the VLVF part succeeded. + */ + + /* Part 1 + * The VFTA is a bitstring made up of 128 32-bit registers + * that enable the particular VLAN id, much like the MTA: + * bits[11-5]: which register + * bits[4-0]: which bit in the register + */ + regindex = (vlan >> 5) & 0x7F; + bitindex = vlan & 0x1F; + targetbit = (1 << bitindex); + // spin_lock_irqsave(&pf_adapter->vlan_setup_lock, flags); + vfta = rd32(hw, RNPM_VFTA(regindex)); + + if (vlan_on) { + if (!(vfta & targetbit)) { + vfta |= targetbit; + vfta_changed = true; + } + } else { + /* donot earase vlan in mutiport */ + if ((vfta & targetbit)) { + vfta &= ~targetbit; + vfta_changed = true; + } + } + + /* to enable two vf have same vlan feature, disable vlvf function. + * as vlan has high-priority than mac-address filter, which means + * two vf can't have same vlan. + */ + + if (vfta_changed) + wr32(hw, RNPM_VFTA(regindex), vfta); + // spin_unlock_irqrestore(&pf_adapter->vlan_setup_lock, flags); + return 0; +} + +/** + * rnpm_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to hardware structure + * + * Clears the VLAN filer table, and the VMDq index associated with the filter + **/ +s32 rnpm_clear_vfta_generic(struct rnpm_hw *hw) +{ + u32 offset; + + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + /* clear vlan table not in mutiport mode */ + if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) { + for (offset = 0; offset < hw->mac.vft_size; offset++) + wr32(hw, RNPM_VFTA(offset), 0); + for (offset = 0; offset < RNPM_VLVF_ENTRIES; offset++) + wr32(hw, RNPM_VLVF(offset), 0); + } + return 0; +} + +/** + * rnpm_check_mac_link_generic - Determine link and speed status + * @hw: pointer to hardware structure + * @speed: pointer to link speed + * @link_up: true when link is up + * @link_up_wait_to_complete: bool used to wait for link up or not + * + * Reads the links register to determine if link is up and the current speed + **/ +s32 rnpm_check_mac_link_generic(struct rnpm_hw *hw, rnpm_link_speed *speed, + bool *link_up, bool link_up_wait_to_complete) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + + rnpm_logd(LOG_FUNC_ENTER, "enter %s %s\n", __func__, + adapter->netdev->name); +#ifdef NO_MBX_VERSION + struct rnpm_pcs_info *pcs = &hw->pcs; +#endif + /* always assume link is up, if no check link function */ + // u32 status; + // u8 port = adapter->port; + + // TRACE(); +#ifdef NO_MBX_VERSION + status = pcs->ops.read(hw, port, RNPM_PCS_LINK_STATUS); + + if (status & RNPM_PCS_LINKUP) + *link_up = true; + else + *link_up = false; + + status = pcs->ops.read(hw, port, RNPM_PCS_LINK_SPEED); + + if (status & RNPM_PCS_1G_OR_10G) { + // 10G mode + switch (status & RNPM_PCS_SPPEED_MASK) { + case RNPM_PCS_SPPEED_10G: + *speed = RNPM_LINK_SPEED_10GB_FULL; + break; + case RNPM_PCS_SPPEED_40G: + *speed = RNPM_LINK_SPEED_40GB_FULL; + break; + } + } + adapter->link_speed = *speed; +#else + hw->speed = adapter->speed; + if (hw->speed == 10) + *speed = RNPM_LINK_SPEED_10_FULL; + else if (hw->speed == 100) + *speed = RNPM_LINK_SPEED_100_FULL; + else if (hw->speed == 1000) + *speed = RNPM_LINK_SPEED_1GB_FULL; + else if (hw->speed == 10000) + *speed = RNPM_LINK_SPEED_10GB_FULL; + else if (hw->speed == 40000) + *speed = RNPM_LINK_SPEED_40GB_FULL; + else + *speed = RNPM_LINK_SPEED_UNKNOWN; + *link_up = hw->link; +#endif + + // #if CONFIG_RNPM_FPGA + // /* used to simulate link down */ + // if (adapter->priv_flags & RNPM_PRIV_FLAG_SIMUATE_DOWN) { + // dbg("simulate link is down\n"); + // *link_up = false; + // *speed = RNPM_LINK_SPEED_UNKNOWN; + // } else { + // *link_up = true; + // *speed = RNPM_LINK_SPEED_10GB_FULL; + // } + // #else + // link_up = false; + // #endif + rnpm_logd(LOG_FUNC_ENTER, "exit %s %s\n", __func__, + adapter->netdev->name); + + return 0; +} + +/** + * rnpm_get_wwn_prefix_generic - Get alternative WWNN/WWPN prefix from + * the EEPROM + * @hw: pointer to hardware structure + * @wwnn_prefix: the alternative WWNN prefix + * @wwpn_prefix: the alternative WWPN prefix + * + * This function will read the EEPROM from the alternative SAN MAC address + * block to check the support for the alternative WWNN/WWPN prefix support. + **/ +s32 rnpm_get_wwn_prefix_generic(struct rnpm_hw *hw, u16 *wwnn_prefix, + u16 *wwpn_prefix) +{ + return 0; +} + +/** + * rnpm_set_mac_anti_spoofing - Enable/Disable MAC anti-spoofing + * @hw: pointer to hardware structure + * @enable: enable or disable switch for anti-spoofing + * @pf: Physical Function pool - do not enable anti-spoofing for the PF + * + **/ +void rnpm_set_mac_anti_spoofing(struct rnpm_hw *hw, bool enable, int pf) +{ +} + +/** + * rnpm_set_vlan_anti_spoofing - Enable/Disable VLAN anti-spoofing + * @hw: pointer to hardware structure + * @enable: enable or disable switch for VLAN anti-spoofing + * @pf: Virtual Function pool - VF Pool to set for VLAN anti-spoofing + * + **/ +void rnpm_set_vlan_anti_spoofing(struct rnpm_hw *hw, bool enable, int vf) +{ +} + +/** + * rnpm_get_device_caps_generic - Get additional device capabilities + * @hw: pointer to hardware structure + * @device_caps: the EEPROM word with the extra device capabilities + * + * This function will read the EEPROM location for the device capabilities, + * and return the word through device_caps. + **/ +s32 rnpm_get_device_caps_generic(struct rnpm_hw *hw, u16 *device_caps) +{ + return 0; +} + +/** + * rnpm_set_rxpba_generic - Initialize RX packet buffer + * @hw: pointer to hardware structure + * @num_pb: number of packet buffers to allocate + * @headroom: reserve n KB of headroom + * @strategy: packet buffer allocation strategy + **/ +void rnpm_set_rxpba_generic(struct rnpm_hw *hw, int num_pb, u32 headroom, + int strategy) +{ +} + +/** + * rnpm_calculate_checksum - Calculate checksum for buffer + * @buffer: pointer to EEPROM + * @length: size of EEPROM to calculate a checksum for + * + * Calculates the checksum for some buffer on a specified length. The + * checksum calculated is returned. + **/ +__maybe_unused static u8 rnpm_calculate_checksum(u8 *buffer, u32 length) +{ + u32 i; + u8 sum = 0; + + if (!buffer) + return 0; + + for (i = 0; i < length; i++) + sum += buffer[i]; + + return (u8)(0 - sum); +} + +/** + * rnpm_host_interface_command - Issue command to manageability block + * @hw: pointer to the HW structure + * @buffer: contains the command to write and where the return status will + * be placed + * @length: length of buffer, must be multiple of 4 bytes + * + * Communicates with the manageability block. On success return 0 + * else return RNPM_ERR_HOST_INTERFACE_COMMAND. + **/ +__maybe_unused static s32 rnpm_host_interface_command(struct rnpm_hw *hw, + u32 *buffer, u32 length) +{ + return -1; +} + +/** + * rnpm_set_fw_drv_ver_generic - Sends driver version to firmware + * @hw: pointer to the HW structure + * @maj: driver version major number + * @min: driver version minor number + * @build: driver version build number + * @sub: driver version sub build number + * + * Sends driver version number to firmware through the manageability + * block. On success return 0 + * else returns RNPM_ERR_SWFW_SYNC when encountering an error acquiring + * semaphore or RNPM_ERR_HOST_INTERFACE_COMMAND when command fails. + **/ +s32 rnpm_set_fw_drv_ver_generic(struct rnpm_hw *hw, u8 maj, u8 min, u8 build, + u8 sub) +{ + return -1; +} + +/** + * rnpm_clear_tx_pending - Clear pending TX work from the PCIe fifo + * @hw: pointer to the hardware structure + * + * The n10 and x540 MACs can experience issues if TX work is still pending + * when a reset occurs. This function prevents this by flushing the PCIe + * buffers on the system. + **/ +void rnpm_clear_tx_pending(struct rnpm_hw *hw) +{ + // u32 gcr_ext, hlreg0; + + /* If double reset is not requested then all transactions should + * already be clear and as such there is no work to do + */ + if (!(hw->mac.mac_flags & RNPM_FLAGS_DOUBLE_RESET_REQUIRED)) + return; +} + +/** + * rnpm_get_thermal_sensor_data_generic - Gathers thermal sensor data + * @hw: pointer to hardware structure + * + * Returns the thermal sensor data structure + **/ +s32 rnpm_get_thermal_sensor_data_generic(struct rnpm_hw *hw) +{ + int voltage = 0; + struct rnpm_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + data->sensor[0].temp = rnpm_mbx_get_temp(hw, &voltage); + + return 0; +} + +/** + * rnpm_init_thermal_sensor_thresh_generic - Inits thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Inits the thermal sensor thresholds according to the NVM map + * and save off the threshold and location values into mac.thermal_sensor_data + **/ +s32 rnpm_init_thermal_sensor_thresh_generic(struct rnpm_hw *hw) +{ + u8 i; + struct rnpm_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + for (i = 0; i < RNPM_MAX_SENSORS; i++) { + data->sensor[i].location = i + 1; + data->sensor[i].caution_thresh = 100; + data->sensor[i].max_op_thresh = 115; + } + + return 0; +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_common.h b/drivers/net/ethernet/mucse/rnpm/rnpm_common.h new file mode 100644 index 000000000000..e0f1598968f0 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_common.h @@ -0,0 +1,372 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_COMMON_H_ +#define _RNPM_COMMON_H_ + +#include +#include +#include +#include "rnpm_type.h" +#include "rnpm.h" +#include "rnpm_regs.h" + +struct rnpm_adapter; + +void rnpm_free_msix_vectors(struct rnpm_adapter *adapter); +int rnpm_acquire_msix_vectors(struct rnpm_adapter *adapter, int vectors); +s32 rnpm_init_eeprom_params_generic(struct rnpm_hw *hw); +// u16 rnpm_get_pcie_msix_count_generic(struct rnpm_hw *hw); +s32 rnpm_init_ops_generic(struct rnpm_hw *hw); +s32 rnpm_init_hw_generic(struct rnpm_hw *hw); +void rnpm_reset_msix_table_generic(struct rnpm_hw *hw); +s32 rnpm_start_hw_generic(struct rnpm_hw *hw); +s32 rnpm_start_hw_gen2(struct rnpm_hw *hw); +s32 rnpm_clear_hw_cntrs_generic(struct rnpm_hw *hw); +s32 rnpm_read_pba_string_generic(struct rnpm_hw *hw, u8 *pba_num, + u32 pba_num_size); +s32 rnpm_get_mac_addr_generic(struct rnpm_hw *hw, u8 *mac_addr); +s32 rnpm_get_permtion_mac_addr(struct rnpm_hw *hw, u8 *mac_addr); +enum rnpm_bus_width rnpm_convert_bus_width(u16 link_status); +enum rnpm_bus_speed rnpm_convert_bus_speed(u16 link_status); +s32 rnpm_get_bus_info_generic(struct rnpm_hw *hw); +void rnpm_set_lan_id_multi_port_pcie(struct rnpm_hw *hw); +s32 rnpm_stop_adapter_generic(struct rnpm_hw *hw); +s32 rnpm_led_on_generic(struct rnpm_hw *hw, u32 index); +s32 rnpm_led_off_generic(struct rnpm_hw *hw, u32 index); +s32 rnpm_init_eeprom_params_generic(struct rnpm_hw *hw); +s32 rnpm_write_eeprom_generic(struct rnpm_hw *hw, u16 offset, u16 data); +s32 rnpm_write_eeprom_buffer_bit_bang_generic(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data); +s32 rnpm_read_eerd_generic(struct rnpm_hw *hw, u16 offset, u16 *data); +s32 rnpm_read_eerd_buffer_generic(struct rnpm_hw *hw, u16 offset, u16 words, + u16 *data); +s32 rnpm_write_eewr_generic(struct rnpm_hw *hw, u16 offset, u16 data); +s32 rnpm_write_eewr_buffer_generic(struct rnpm_hw *hw, u16 offset, u16 words, + u16 *data); +s32 rnpm_read_eeprom_bit_bang_generic(struct rnpm_hw *hw, u16 offset, + u16 *data); +s32 rnpm_read_eeprom_buffer_bit_bang_generic(struct rnpm_hw *hw, u16 offset, + u16 words, u16 *data); +u16 rnpm_calc_eeprom_checksum_generic(struct rnpm_hw *hw); +s32 rnpm_validate_eeprom_checksum_generic(struct rnpm_hw *hw, + u16 *checksum_val); +s32 rnpm_update_eeprom_checksum_generic(struct rnpm_hw *hw); +s32 rnpm_set_rar_generic(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq, + u32 enable_addr); +s32 rnpm_set_rar_mac(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq, + u32 port); +s32 rnpm_clear_rar_generic(struct rnpm_hw *hw, u32 index); +s32 rnpm_clear_rar_mac(struct rnpm_hw *hw, u32 index, u32 port); +s32 rnpm_init_rx_addrs_generic(struct rnpm_hw *hw); +s32 rnpm_update_mc_addr_list_generic(struct rnpm_hw *hw, + struct net_device *netdev); +s32 rnpm_update_mutiport_mc_addr_list_generic(struct rnpm_hw *hw, + struct net_device *netdev); +s32 rnpm_enable_mc_generic(struct rnpm_hw *hw); +s32 rnpm_disable_mc_generic(struct rnpm_hw *hw); +s32 rnpm_disable_rx_buff_generic(struct rnpm_hw *hw); +s32 rnpm_enable_rx_buff_generic(struct rnpm_hw *hw); +s32 rnpm_enable_rx_dma_generic(struct rnpm_hw *hw, u32 regval); +s32 rnpm_fc_enable_generic(struct rnpm_hw *hw); +s32 rnpm_setup_fc(struct rnpm_hw *hw); +bool rnpm_device_supports_autoneg_fc(struct rnpm_hw *hw); +void rnpm_fc_autoneg(struct rnpm_hw *hw); +s32 rnpm_acquire_swfw_sync(struct rnpm_hw *hw, u16 mask); +void rnpm_release_swfw_sync(struct rnpm_hw *hw, u16 mask); +s32 rnpm_get_san_mac_addr_generic(struct rnpm_hw *hw, u8 *san_mac_addr); +s32 rnpm_set_vmdq_generic(struct rnpm_hw *hw, u32 rar, u32 vmdq); +s32 rnpm_set_vmdq_san_mac_generic(struct rnpm_hw *hw, u32 vmdq); +s32 rnpm_clear_vmdq_generic(struct rnpm_hw *hw, u32 rar, u32 vmdq); +s32 rnpm_init_uta_tables_generic(struct rnpm_hw *hw); +s32 rnpm_set_vfta_generic(struct rnpm_hw *hw, u32 vlan, u32 vind, bool vlan_on); +s32 rnpm_set_vfta_mac_generic(struct rnpm_hw *hw, u32 vlan, u32 vind, + bool vlan_on); +void rnpm_ncsi_set_mc_mta_generic(struct rnpm_hw *hw); +void rnpm_ncsi_set_vfta_mac_generic(struct rnpm_hw *hw); +void rnpm_ncsi_set_uc_addr_generic(struct rnpm_hw *hw); + +s32 rnpm_clear_vfta_generic(struct rnpm_hw *hw); +s32 rnpm_check_mac_link_generic(struct rnpm_hw *hw, rnpm_link_speed *speed, + bool *link_up, bool link_up_wait_to_complete); +s32 rnpm_get_wwn_prefix_generic(struct rnpm_hw *hw, u16 *wwnn_prefix, + u16 *wwpn_prefix); +s32 rnpm_blink_led_start_generic(struct rnpm_hw *hw, u32 index); +s32 rnpm_blink_led_stop_generic(struct rnpm_hw *hw, u32 index); +void rnpm_set_mac_anti_spoofing(struct rnpm_hw *hw, bool enable, int pf); +void rnpm_set_vlan_anti_spoofing(struct rnpm_hw *hw, bool enable, int vf); +s32 rnpm_get_device_caps_generic(struct rnpm_hw *hw, u16 *device_caps); +s32 rnpm_set_fw_drv_ver_generic(struct rnpm_hw *hw, u8 maj, u8 min, u8 build, + u8 ver); +void rnpm_clear_tx_pending(struct rnpm_hw *hw); +void rnpm_set_rxpba_generic(struct rnpm_hw *hw, int num_pb, u32 headroom, + int strategy); +s32 rnpm_reset_pipeline_82599(struct rnpm_hw *hw); + +s32 rnpm_get_thermal_sensor_data_generic(struct rnpm_hw *hw); +s32 rnpm_init_thermal_sensor_thresh_generic(struct rnpm_hw *hw); + +/*================= registers read/write helper ===== */ +#define p_rnpm_wr_reg(reg, val) \ + do { \ + printk(KERN_DEBUG "wr-reg: %p <== 0x%08x \t#%-4d %s\n", (reg), \ + (val), __LINE__, __FILE__); \ + iowrite32((val), (void *)(reg)); \ + } while (0) + +static inline int prnp_rd_reg(void *reg) +{ + int v = ioread32((void *)(reg)); + + printk(KERN_DEBUG "rd-reg: %p ==> 0x%08x\n", reg, v); + return v; +} + +#ifdef IO_PRINT +static inline unsigned int rnpm_rd_reg(void *reg) +{ + unsigned int v = ioread32((void *)(reg)); + + printk(KERN_DEBUG "rd-reg: %p ==> 0x%08x\n", reg, v); + return v; +} + +#define rnpm_wr_reg(reg, val) \ + do { \ + printk(KERN_DEBUG "wr-reg: %p <== 0x%08x \t#%-4d %s\n", (reg), \ + (val), __LINE__, __FILE__); \ + iowrite32((val), (void *)(reg)); \ + } while (0) +#else +#define rnpm_rd_reg(reg) readl((void *)(reg)) +#define rnpm_wr_reg(reg, val) writel((val), (void *)(reg)) +#endif + +#define rd32(hw, off) rnpm_rd_reg((hw)->hw_addr + (off)) +#define wr32(hw, off, val) rnpm_wr_reg((hw)->hw_addr + (off), (val)) + +#define ring_rd32(ring, off) rnpm_rd_reg((ring)->dma_hw_addr + (off)) +#define ring_wr32(ring, off, val) rnpm_wr_reg((ring)->dma_hw_addr + (off), val) + +#define pwr32(hw, off, val) p_rnpm_wr_reg((hw)->hw_addr + (off), (val)) +#define rnpm_mbx_rd(hw, off) rnpm_rd_reg((hw)->ring_msix_base + (off)) +#define rnpm_mbx_wr(hw, off, val) rnpm_wr_reg((hw)->ring_msix_base + (off), val) + +static inline void hw_queue_strip_rx_vlan(struct rnpm_hw *hw, u8 ring_num, + bool enable) +{ + u32 reg = RNPM_ETH_VLAN_VME_REG(ring_num / 32); + u32 offset = ring_num % 32; + u32 data = rd32(hw, reg); + + if (enable == true) + data |= (1 << offset); + else + data &= ~(1 << offset); + wr32(hw, reg, data); +} + +#define rnpm_set_reg_bit(hw, reg_def, bit) \ + do { \ + u32 reg = reg_def; \ + u32 value = rd32(hw, reg); \ + dbg("before set %x %x\n", reg, value); \ + value |= (0x01 << bit); \ + dbg("after set %x %x\n", reg, value); \ + wr32(hw, reg, value); \ + } while (0) + +#define rnpm_clr_reg_bit(hw, reg_def, bit) \ + do { \ + u32 reg = reg_def; \ + u32 value = rd32(hw, reg); \ + dbg("before clr %x %x\n", reg, value); \ + value &= (~(0x01 << bit)); \ + dbg("after clr %x %x\n", reg, value); \ + wr32(hw, reg, value); \ + } while (0) + +#define rnpm_vlan_filter_on(hw) \ + rnpm_set_reg_bit(hw, RNPM_ETH_VLAN_FILTER_ENABLE, 30) +#define rnpm_vlan_filter_off(hw) \ + rnpm_clr_reg_bit(hw, RNPM_ETH_VLAN_FILTER_ENABLE, 30) + +#define DPRINTK(nlevel, klevel, fmt, args...) \ + ((NETIF_MSG_##nlevel & adapter->msg_enable) ? \ + (void)(netdev_printk(KERN_##klevel, adapter->netdev, fmt, \ + ##args)) : \ + NULL) + +//==== log helper === +#ifdef HW_DEBUG +#define hw_dbg(hw, fmt, args...) printk(KERN_DEBUG "hw-dbg : " fmt, ##args) +#else +#define hw_dbg(hw, fmt, args...) +#endif + +#ifdef PTP_DEBUG +#define ptp_dbg(fmt, args...) printk(KERN_DEBUG "ptp-dbg : " fmt, ##args) +#else +#define ptp_dbg(fmt, args...) +#endif + +// netdev_dbg(((struct rnpm_adapter *)((hw)->back))->netdev, format, ##arg) +#ifdef RNP_DEBUG_OPEN +#define rnpm_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) +#else +#define rnpm_dbg(fmt, args...) +#endif + +#define rnpm_info(fmt, args...) printk(KERN_DEBUG "rnpm-info: " fmt, ##args) +#define rnpm_warn(fmt, args...) printk(KERN_DEBUG "rnpm-warn: " fmt, ##args) +#define rnpm_err(fmt, args...) printk(KERN_DEBUG "rnpm-err : " fmt, ##args) + +#define e_info(msglvl, format, arg...) \ + netif_info(adapter, msglvl, adapter->netdev, format, ##arg) +#define e_err(msglvl, format, arg...) \ + netif_err(adapter, msglvl, adapter->netdev, format, ##arg) +#define e_warn(msglvl, format, arg...) \ + netif_warn(adapter, msglvl, adapter->netdev, format, ##arg) +#define e_crit(msglvl, format, arg...) \ + netif_crit(adapter, msglvl, adapter->netdev, format, ##arg) + +#define e_dev_info(format, arg...) dev_info(&adapter->pdev->dev, format, ##arg) +#define e_dev_warn(format, arg...) dev_warn(&adapter->pdev->dev, format, ##arg) +#define e_dev_err(format, arg...) dev_err(&adapter->pdev->dev, format, ##arg) + +#define buf_dump_line(msg, line, buf, len) + +static inline void buf_dump(const char *msg, void *buf, int len) +{ + int i, offset = 0; + int msg_len = 512; + u8 msg_buf[512]; + u8 *ptr = (u8 *)buf; + + offset += snprintf(msg_buf + offset, msg_len, + "=== %s #%d ==\n000: ", msg, len); + + for (i = 0; i < len; ++i) { + if ((i != 0) && (i % 16) == 0 && (offset >= (512 - 10 * 16))) { + printk(KERN_DEBUG "%s\n", msg_buf); + offset = 0; + } + if ((i != 0) && (i % 16) == 0) + offset += snprintf(msg_buf + offset, msg_len, + "\n%03x: ", i); + + offset += snprintf(msg_buf + offset, msg_len, "%02x ", ptr[i]); + } + offset += snprintf(msg_buf + offset, msg_len, "\n=== done ==\n"); + printk(KERN_DEBUG "%s\n", msg_buf); +} + +#define TRACE() printk(KERN_DEBUG "==[ %s %d ] ==\n", __func__, __LINE__) + +#ifdef CONFIG_RNPM_RX_DEBUG +#define rx_debug_printk printk +#define rx_buf_dump buf_dump +#define rx_dbg(fmt, args...) \ + printk(KERN_DEBUG "KERN_DEBUG [ %s:%d ] " fmt, __func__, __LINE__, \ + ##args) +#else /* CONFIG_RNPM_RX_DEBUG */ +#define rx_debug_printk(fmt, args...) +#define rx_buf_dump(a, b, c) +#define rx_dbg(fmt, args...) +#endif /* CONFIG_RNPM_RX_DEBUG */ + +#define desc_hex_dump(msg, buf, len) +#define tx_dbg(fmt, args...) + +#ifdef DEBUG +#define dbg(fmt, args...) \ + printk(KERN_DEBUG "[ %s:%d ] " fmt, __func__, __LINE__, ##args) +#else +#define dbg(fmt, args...) \ + do { \ + } while (0) +#endif + +#ifdef DEBUG_DRECTION +#define drection_dbg(fmt, arg...) \ + printk(KERN_DEBUG "[ %s:%d ] " fmt, __func__, __LINE__, ##args) +#else +#define drection_dbg(fmt, arg...) +#endif + +#ifdef CONFIG_RNPM_VF_DEBUG +#define vf_dbg(fmt, args...) \ + printk(KERN_DEBUG "[ %s:%d ] " fmt, __func__, __LINE__, ##args) +#else +#define vf_dbg(fmt, args...) +#endif + +enum RNP_LOG_EVT { + LOG_MBX_IN, + LOG_MBX_OUT, + LOG_MBX_MSG_IN, + LOG_MBX_MSG_OUT, + LOG_LINK_EVENT, + LOG_ADPT_STAT, + LOG_MBX_ABLI, + LOG_MBX_LINK_STAT, + LOG_MBX_IFUP_DOWN, + LOG_FUNC_ENTER, + LOG_SET_LANE_FUN, + LOG_PTP_EVT, + LOG_MBX_REQ, + LOG_MBX_LOCK, + LOG_ETHTOOL, + LOG_PHY, +}; + +extern unsigned int rnpm_loglevel; + +#define rnpm_logd(evt, fmt, args...) \ + do { \ + if (BIT(evt) & rnpm_loglevel) { \ + printk(KERN_DEBUG fmt, ##args); \ + } \ + } while (0) + +#define rnpm_logd_level(bit) ((1 << (bit)) & rnpm_loglevel) + +static inline u64 rnpm_recalculate_err_pkts(u64 now, u64 *init, bool is_u64) +{ + u64 data = 0; + + if (now >= *init) + data = now - *init; + else + data = is_u64 ? (u64)-1 - *init + now : (u32)-1 - *init + now; + *init = now; + + return data; +} + +static inline uint32_t rnpm_vid_crc32_le(uint16_t vid_le) +{ + uint8_t *data = (unsigned char *)&vid_le; + uint8_t data_byte = 0; + uint32_t crc = ~0x0; + uint32_t temp = 0; + int i, bits; +#define RNPM_VLAN_VID_MASK (0x0fff) + + bits = get_bitmask_order(RNPM_VLAN_VID_MASK); + for (i = 0; i < bits; i++) { + if ((i % 8) == 0) + data_byte = data[i / 8]; + + temp = ((crc & 1) ^ data_byte) & 1; + crc >>= 1; + data_byte >>= 1; + + if (temp) + crc ^= 0xedb88320; + } + + return crc; +} + +#endif /* RNPM_COMMON */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_debugfs.c b/drivers/net/ethernet/mucse/rnpm/rnpm_debugfs.c new file mode 100644 index 000000000000..97c0c5e00f2f --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_debugfs.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ +#include +#include + +#include "rnpm.h" +#include "rnpm_mbx_fw.h" + +#ifdef CONFIG_DEBUG_FS +static struct dentry *rnpm_dbg_root; + +static char rnpm_dbg_reg_ops_buf[256] = ""; + +/** + * rnpm_dbg_reg_ops_read - read for reg_ops datum + * @filp: the opened file + * @buffer: where to write the data for the user to read + * @count: the size of the user's buffer + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_reg_ops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + char *buf; + int len; + + /* don't allow partial reads */ + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "%s: %s\n", adapter->name, + rnpm_dbg_reg_ops_buf); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + + kfree(buf); + return len; +} + +/** + * rnpm_dbg_reg_ops_write - write into reg_ops datum + * @filp: the opened file + * @buffer: where to find the user's data + * @count: the length of the user's data + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_reg_ops_write(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + struct rnpm_hw *hw = &adapter->hw; + int len; + + /* don't allow partial writes */ + if (*ppos != 0) + return 0; + if (count >= sizeof(rnpm_dbg_reg_ops_buf)) + return -ENOSPC; + + len = simple_write_to_buffer(rnpm_dbg_reg_ops_buf, + sizeof(rnpm_dbg_reg_ops_buf) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + rnpm_dbg_reg_ops_buf[len] = '\0'; + + if (strncmp(rnpm_dbg_reg_ops_buf, "write", 5) == 0) { + u32 reg, value; + int cnt; + + cnt = sscanf(&rnpm_dbg_reg_ops_buf[5], "%x %x", ®, &value); + if (cnt == 2) { + if (reg >= 0x30000000) { + rnpm_mbx_reg_write(hw, reg, value); + } else { + rnpm_wr_reg(hw->hw_addr + reg, value); + value = rnpm_rd_reg(hw->hw_addr + reg); + } + e_dev_info("write: 0x%08x = 0x%08x\n", reg, value); + } else { + e_dev_info("write \n"); + } + } else if (strncmp(rnpm_dbg_reg_ops_buf, "read", 4) == 0) { + u32 reg, value; + int cnt; + + cnt = sscanf(&rnpm_dbg_reg_ops_buf[4], "%x", ®); + if (cnt == 1) { + if (reg >= 0x30000000) + value = rnpm_mbx_fw_reg_read(hw, reg); + else + value = rnpm_rd_reg(hw->hw_addr + reg); + snprintf(rnpm_dbg_reg_ops_buf, + sizeof(rnpm_dbg_reg_ops_buf), "0x%08x: 0x%08x", + reg, value); + e_dev_info("read 0x%08x = 0x%08x\n", reg, value); + } else { + e_dev_info("read \n"); + } + } else { + e_dev_info("Unknown command %s\n", rnpm_dbg_reg_ops_buf); + e_dev_info("Available commands:\n"); + e_dev_info(" read \n"); + e_dev_info(" write \n"); + } + return count; +} + +static const struct file_operations rnpm_dbg_reg_ops_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = rnpm_dbg_reg_ops_read, + .write = rnpm_dbg_reg_ops_write, +}; + +static char rnpm_dbg_netdev_ops_buf[256] = ""; + +/** + * rnpm_dbg_netdev_ops_read - read for netdev_ops datum + * @filp: the opened file + * @buffer: where to write the data for the user to read + * @count: the size of the user's buffer + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_netdev_ops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + // struct rnpm_hw *hw = &adapter->hw; + char *buf; + int len; + + /* don't allow partial reads */ + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "%s: %s\n", adapter->name, + rnpm_dbg_netdev_ops_buf); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + + kfree(buf); + return len; +} + +/** + * rnpm_dbg_netdev_ops_write - write into netdev_ops datum + * @filp: the opened file + * @buffer: where to find the user's data + * @count: the length of the user's data + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_netdev_ops_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + int len; + + /* don't allow partial writes */ + if (*ppos != 0) + return 0; + if (count >= sizeof(rnpm_dbg_netdev_ops_buf)) + return -ENOSPC; + + len = simple_write_to_buffer(rnpm_dbg_netdev_ops_buf, + sizeof(rnpm_dbg_netdev_ops_buf) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + rnpm_dbg_netdev_ops_buf[len] = '\0'; + + if (strncmp(rnpm_dbg_netdev_ops_buf, "stat", 4) == 0) { + rnpm_info("adapter->stat=0x%lx\n", adapter->state); + rnpm_info("adapter->tx_timeout_count=%d\n", + adapter->tx_timeout_count); + } else if (strncmp(rnpm_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) { + adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev, + UINT_MAX); + e_dev_info("tx_timeout called\n"); + } else { + e_dev_info("Unknown command: %s\n", rnpm_dbg_netdev_ops_buf); + e_dev_info("Available commands:\n"); + e_dev_info(" tx_timeout\n"); + } + return count; +} + +static const struct file_operations rnpm_dbg_netdev_ops_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = rnpm_dbg_netdev_ops_read, + .write = rnpm_dbg_netdev_ops_write, +}; + +static char rnpm_dbg_phy_ops_buf[256] = ""; + +/** + * rnpm_dbg_phy_ops_read - read for reg_ops datum + * @filp: the opened file + * @buffer: where to write the data for the user to read + * @count: the size of the user's buffer + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_phy_ops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + char *buf; + int len; + + /* don't allow partial reads */ + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "%s: %s\n", adapter->name, + rnpm_dbg_phy_ops_buf); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + + kfree(buf); + return len; +} + +/** + * rnpm_dbg_reg_ops_write - write into reg_ops datum + * @filp: the opened file + * @buffer: where to find the user's data + * @count: the length of the user's data + * @ppos: file position offset + **/ +static ssize_t rnpm_dbg_phy_ops_write(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rnpm_adapter *adapter = filp->private_data; + struct rnpm_hw *hw = &adapter->hw; + int len; + + /* don't allow partial writes */ + if (*ppos != 0) + return 0; + if (count >= sizeof(rnpm_dbg_phy_ops_buf)) + return -ENOSPC; + + len = simple_write_to_buffer(rnpm_dbg_phy_ops_buf, + sizeof(rnpm_dbg_phy_ops_buf) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + rnpm_dbg_phy_ops_buf[len] = '\0'; + + if (strncmp(rnpm_dbg_phy_ops_buf, "write", 5) == 0) { + u32 reg, value; + int cnt; + + cnt = sscanf(&rnpm_dbg_phy_ops_buf[5], "%x %x", ®, &value); + + if (cnt == 2) { + if (rnpm_mbx_phy_write(hw, reg, value) == 0) + e_dev_info("write phy: 0x%08x = 0x%08x\n", reg, + value); + else + e_dev_info( + "write phy failed: 0x%08x = 0x%08x\n", + reg, value); + } else { + e_dev_info("write phy \n"); + } + + } else if (strncmp(rnpm_dbg_phy_ops_buf, "read", 4) == 0) { + u32 reg, value; + int cnt; + + cnt = sscanf(&rnpm_dbg_phy_ops_buf[4], "%x", ®); + if (cnt == 1) { + if (rnpm_mbx_phy_read(hw, reg, &value) == 0) { + sprintf(rnpm_dbg_phy_ops_buf, + "read phy 0x%08x = 0x%08x\n", reg, + value); + rnpm_info("read phy 0x%08x = 0x%08x\n", reg, + value); + } else + e_dev_info("read phy failed 0x%08x = 0x%08x\n", + reg, value); + } else { + e_dev_info("read phy \n"); + } + } else { + e_dev_info("Unknown command %s\n", rnpm_dbg_phy_ops_buf); + e_dev_info("Available commands:\n"); + e_dev_info(" read \n"); + e_dev_info(" write \n"); + } + return count; +} + +static const struct file_operations rnpm_dbg_phy_ops_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = rnpm_dbg_phy_ops_read, + .write = rnpm_dbg_phy_ops_write, +}; + +/** + * rnpm_dbg_adapter_init - setup the debugfs directory for the adapter + * @adapter: the adapter that is starting up + **/ +void rnpm_dbg_adapter_init(struct rnpm_adapter *adapter) +{ + const char *name = adapter->name; + struct dentry *pfile; + + adapter->rnpm_dbg_adapter = debugfs_create_dir(name, rnpm_dbg_root); + if (adapter->rnpm_dbg_adapter) { + pfile = debugfs_create_file("reg_ops", 0600, + adapter->rnpm_dbg_adapter, adapter, + &rnpm_dbg_reg_ops_fops); + if (!pfile) + e_dev_err("debugfs reg_ops for %s failed\n", name); + pfile = debugfs_create_file("netdev_ops", 0600, + adapter->rnpm_dbg_adapter, adapter, + &rnpm_dbg_netdev_ops_fops); + if (!pfile) + e_dev_err("debugfs netdev_ops for %s failed\n", name); + pfile = debugfs_create_file("phy_ops", 0600, + adapter->rnpm_dbg_adapter, adapter, + &rnpm_dbg_phy_ops_fops); + if (!pfile) + e_dev_err("debugfs netdev_ops for %s failed\n", name); + } else { + e_dev_err("debugfs entry for %s failed\n", name); + } +} + +/** + * rnpm_dbg_adapter_exit - clear out the adapter's debugfs entries + * @pf: the pf that is stopping + **/ +void rnpm_dbg_adapter_exit(struct rnpm_adapter *adapter) +{ + debugfs_remove_recursive(adapter->rnpm_dbg_adapter); + adapter->rnpm_dbg_adapter = NULL; +} + +/** + * rnpm_dbg_init - start up debugfs for the driver + **/ +void rnpm_dbg_init(void) +{ + rnpm_dbg_root = debugfs_create_dir(rnpm_driver_name, NULL); + if (rnpm_dbg_root == NULL) + pr_err("init of debugfs failed\n"); +} + +/** + * rnpm_dbg_exit - clean out the driver's debugfs entries + **/ +void rnpm_dbg_exit(void) +{ + debugfs_remove_recursive(rnpm_dbg_root); +} +#endif diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_ethtool.c b/drivers/net/ethernet/mucse/rnpm/rnpm_ethtool.c new file mode 100644 index 000000000000..c3f40b54fa01 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_ethtool.c @@ -0,0 +1,3335 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +/* ethtool support for N10M */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rnpm.h" +#include "rnpm_mpe.h" +#include "rnpm_mbx.h" +#include "rnpm_phy.h" +#include "rnpm_sriov.h" +#include "rnpm_mbx_fw.h" + +// #ifdef SIOCETHTOOL +#define RNPM_ALL_RAR_ENTRIES 16 + +// #ifdef ETHTOOL_TEST + +enum { NETDEV_STATS, RNPM_STATS }; + +struct rnpm_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +/* rnpm allocates num_tx_queues and num_rx_queues symmetrically so + * we set the num_rx_queues to evaluate to num_tx_queues. This is + * used because we do not have a good way to get the max number of + * rx queues with CONFIG_RPS disabled. + */ +#ifdef HAVE_TX_MQ +#ifdef HAVE_NETDEV_SELECT_QUEUE +#ifdef NO_REAL_QUEUE_NUM +#define RNPM_NUM_RX_QUEUES adapter->num_tx_queues +#define RNPM_NUM_TX_QUEUES adapter->num_tx_queues +#else +#define RNPM_NUM_RX_QUEUES netdev->real_num_rx_queues +#define RNPM_NUM_TX_QUEUES netdev->real_num_tx_queues + +#endif /* NO_REAL_QUEUE_NUM */ +#else +#define RNPM_NUM_RX_QUEUES adapter->indices +#define RNPM_NUM_TX_QUEUES adapter->indices +#endif /* HAVE_NETDEV_SELECT_QUEUE */ +#else /* HAVE_TX_MQ */ +#define RNPM_NUM_TX_QUEUES 1 +#define RNPM_NUM_RX_QUEUES \ + (((struct rnpm_adapter *)netdev_priv(netdev))->num_rx_queues) +#endif /* HAVE_TX_MQ */ + +#define RNPM_NETDEV_STAT(_net_stat) \ + { \ + .stat_string = #_net_stat, \ + .sizeof_stat = \ + sizeof_field(struct net_device_stats, _net_stat), \ + .stat_offset = offsetof(struct net_device_stats, _net_stat) \ + } +static const struct rnpm_stats rnpm_gstrings_net_stats[] = { + RNPM_NETDEV_STAT(rx_packets), + RNPM_NETDEV_STAT(tx_packets), + RNPM_NETDEV_STAT(rx_bytes), + RNPM_NETDEV_STAT(tx_bytes), + RNPM_NETDEV_STAT(rx_errors), + RNPM_NETDEV_STAT(tx_errors), + RNPM_NETDEV_STAT(rx_dropped), + RNPM_NETDEV_STAT(tx_dropped), + RNPM_NETDEV_STAT(multicast), + RNPM_NETDEV_STAT(collisions), + RNPM_NETDEV_STAT(rx_over_errors), + RNPM_NETDEV_STAT(rx_crc_errors), + RNPM_NETDEV_STAT(rx_frame_errors), + RNPM_NETDEV_STAT(rx_fifo_errors), + RNPM_NETDEV_STAT(rx_missed_errors), + RNPM_NETDEV_STAT(tx_aborted_errors), + RNPM_NETDEV_STAT(tx_carrier_errors), + RNPM_NETDEV_STAT(tx_fifo_errors), + RNPM_NETDEV_STAT(tx_heartbeat_errors), +}; +#define RNPM_GLOBAL_STATS_LEN ARRAY_SIZE(rnpm_gstrings_net_stats) + +#define RNPM_HW_STAT(_name, _stat) \ + { \ + .stat_string = _name, \ + .sizeof_stat = sizeof_field(struct rnpm_adapter, _stat), \ + .stat_offset = offsetof(struct rnpm_adapter, _stat) \ + } +static struct rnpm_stats rnpm_hwstrings_stats[] = { + RNPM_HW_STAT("dma_to_eth", hw_stats.dma_to_eth), + RNPM_HW_STAT("dma_to_switch", hw_stats.dma_to_switch), + // RNPM_HW_STAT("mac_to_mac", hw_stats.mac_to_mac), + // RNPM_HW_STAT("switch_to_switch", hw_stats.switch_to_switch), + RNPM_HW_STAT("eth_to_dma", hw_stats.mac_to_dma), + RNPM_HW_STAT("switch_to_dma", hw_stats.switch_to_dma), + RNPM_HW_STAT("vlan_add_cnt", hw_stats.vlan_add_cnt), + RNPM_HW_STAT("vlan_strip_cnt", hw_stats.vlan_strip_cnt), + //=== drop== + // RNPM_HW_STAT("invalid_droped_packets", hw_stats.invalid_droped_packets), + // RNPM_HW_STAT("filter_dropped_packets", hw_stats.filter_dropped_packets), + // RNPM_HW_STAT("host_l2_match_drop", hw_stats.host_l2_match_drop), + // RNPM_HW_STAT("redir_input_match_drop", hw_stats.redir_input_match_drop), + // RNPM_HW_STAT("redir_etype_match_drop", hw_stats.redir_etype_match_drop), + // RNPM_HW_STAT("redir_tcp_syn_match_drop", + // hw_stats.redir_tcp_syn_match_drop), + // RNPM_HW_STAT("redir_tuple5_match_drop", + // hw_stats.redir_tuple5_match_drop), RNPM_HW_STAT("redir_tcam_match_drop", + // hw_stats.redir_tcam_match_drop), + + // RNPM_HW_STAT("bmc_dropped_packets", hw_stats.bmc_dropped_packets), + // RNPM_HW_STAT("switch_dropped_packets", hw_stats.switch_dropped_packets), + RNPM_HW_STAT("rx_csum_offload_errors", hw_csum_rx_error), + RNPM_HW_STAT("rx_csum_offload_good", hw_csum_rx_good), + RNPM_HW_STAT("rx_broadcast_count", hw_stats.mac_rx_broadcast), + RNPM_HW_STAT("rx_multicast_count", hw_stats.mac_rx_multicast), + RNPM_HW_STAT("mac_rx_pause_cnt", hw_stats.mac_rx_pause_cnt), + RNPM_HW_STAT("mac_tx_pause_cnt", hw_stats.mac_tx_pause_cnt), +}; +#define RNPM_HWSTRINGS_STATS_LEN ARRAY_SIZE(rnpm_hwstrings_stats) + +struct rnpm_tx_queue_ring_stat { + u64 hw_head; + u64 hw_tail; + u64 sw_to_clean; + u64 sw_to_next_to_use; +}; + +struct rnpm_rx_queue_ring_stat { + u64 hw_head; + u64 hw_tail; + u64 sw_to_use; + u64 sw_to_clean; +}; + +#define RNPM_QUEUE_STATS_LEN \ + (RNPM_NUM_TX_QUEUES * \ + (sizeof(struct rnpm_tx_queue_stats) / sizeof(u64) + \ + sizeof(struct rnpm_queue_stats) / sizeof(u64) + \ + sizeof(struct rnpm_tx_queue_ring_stat) / sizeof(u64)) + \ + RNPM_NUM_RX_QUEUES * \ + (sizeof(struct rnpm_rx_queue_stats) / sizeof(u64) + \ + sizeof(struct rnpm_queue_stats) / sizeof(u64) + \ + sizeof(struct rnpm_rx_queue_ring_stat) / sizeof(u64))) + +#define RNPM_STATS_LEN \ + (RNPM_GLOBAL_STATS_LEN + RNPM_HWSTRINGS_STATS_LEN + \ + RNPM_QUEUE_STATS_LEN) +#ifdef ETHTOOL_TEST +static const char rnpm_gstrings_test[][ETH_GSTRING_LEN] = { + "Register test (offline)", "Eeprom test (offline)", + "Interrupt test (offline)", "Loopback test (offline)", + "Link test (on/offline)" +}; + +#define RNPM_TEST_LEN (sizeof(rnpm_gstrings_test) / ETH_GSTRING_LEN) +#else +#define RNPM_TEST_LEN 0 +#endif + +static int rnpm_get_regs_len(struct net_device *netdev) +{ +// #define RNPM_REGS_LEN 1129 +#define RNPM_REGS_LEN 1 + return RNPM_REGS_LEN * sizeof(u32); +} + +static void rnpm_get_regs(struct net_device *netdev, struct ethtool_regs *regs, + void *p) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + u32 *regs_buff = p; + int i; + + memset(p, 0, RNPM_REGS_LEN * sizeof(u32)); + + for (i = 0; i < RNPM_REGS_LEN; i++) + regs_buff[i] = rd32(hw, i * sizeof(u32)); +} + +static const char rnpm_priv_flags_strings[][ETH_GSTRING_LEN] = { +#define RNPM_MAC_LOOPBACK BIT(0) +#define RNPM_SWITCH_LOOPBACK BIT(1) +#define RNPM_VEB_ENABLE BIT(2) +#define RNPM_PCIE_CACHE_ALIGN_PATCH BIT(3) +#define RNPM_PADDING_DEBUG BIT(4) +#define RNPM_PTP_FEATURE BIT(5) +#define RNPM_SIMULATE_DOWN BIT(6) +#define RNPM_TO_RPU BIT(7) +#define RNPM_LEN_ERR BIT(8) +#define RNPM_FW_10G_1G_SFP_AUTO_DET_EN BIT(9) +#define RNPM_MPE_RELOAD BIT(10) +#define RNPM_FORCE_SPEED_ABLITY BIT(11) +#define RNPM_LLDP_EN_STAT BIT(12) + "mac_loopback", + "switch_loopback", + "veb_enable", + "pcie_patch", + "padding_debug", + "ptp_performance_debug", + "simulate_link_down", + "to_rpu", + "mask_len_err", + "fw_10g_1g_auto_det", + "mpe_reload", + "force_speed_ablity", + "lldp_en" +}; + +#define RNPM_PRIV_FLAGS_STR_LEN ARRAY_SIZE(rnpm_priv_flags_strings) + +static const char rnpm_phy_statistics_strings[][ETH_GSTRING_LEN] = { + "RX crc good (64~1518)", "RX crc good (>1518)", + "RX crc good (<64)", "RX crc wrong (64~1518)", + "RX crc wrong (>1518)", "RX crc wrong (<64)", + "RX SFD missed (nosfd)", "TX crc good (64~1518)", + "TX crc good (>1518)", "TX crc good (<64)", + "TX crc wrong (64~1518)", "TX crc wrong (>1518)", + "TX crc wrong (<64)", "TX SFD missed (nosfd)", +}; + +#define RNPM_PHY_STATISTICS_STR_LEN ARRAY_SIZE(rnpm_phy_statistics_strings) + +static void rnpm_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + strscpy(drvinfo->driver, rnpm_driver_name, sizeof(drvinfo->driver)); + snprintf(drvinfo->version, sizeof(drvinfo->version), "%s-%x", + rnpm_driver_version, hw->ccode); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%d.%d 0x%08x", ((char *)&(hw->fw_version))[3], + ((char *)&(hw->fw_version))[2], ((char *)&(hw->fw_version))[1], + ((char *)&(hw->fw_version))[0], hw->fw_uid); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->n_stats = RNPM_STATS_LEN; + drvinfo->testinfo_len = RNPM_TEST_LEN; + drvinfo->regdump_len = rnpm_get_regs_len(netdev); + drvinfo->n_priv_flags = RNPM_PRIV_FLAGS_STR_LEN; +} + +static int rnpm_set_autoneg_adv_from_hw(struct rnpm_hw *hw, + struct ethtool_link_ksettings *ks) +{ + /* Read autoneg state from phy */ + if (hw->phy_type == PHY_TYPE_SGMII) { + /* Not support AN, return directly */ + if (!(hw->phy.vb_r[0] & BIT(12)) || !hw->link) + return 0; + + if (hw->phy.vb_r[4] & 0x100) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Full); + if (hw->phy.vb_r[4] & 0x80) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Half); + if (hw->phy.vb_r[4] & 0x40) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10baseT_Full); + if (hw->phy.vb_r[4] & 0x20) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10baseT_Half); + + if (hw->phy.vb_r[9] & 0x200) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + if (hw->phy.vb_r[9] & 0x100) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Half); + } + + return 0; +} + +/** + * rnpm_phy_type_to_ethtool - convert the phy_types to ethtool link modes + * @adapter: adapter struct with hw->phy_type + * @ks: ethtool link ksettings struct to fill out + * + **/ +static void rnpm_phy_type_to_ethtool(struct rnpm_adapter *adapter, + struct ethtool_link_ksettings *ks) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 supported_link = hw->supported_link; + u8 phy_type = hw->phy_type; + + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + rnpm_logd(LOG_ETHTOOL, + "phy_type_to_ethtool name=%s link=%d speed=%d phy-type=0x%x ", + adapter->netdev->name, hw->link, hw->speed, phy_type); + rnpm_logd(LOG_ETHTOOL, + "sopport-link=0x%x media=0x%x priv_flags=0x%x\n ", + supported_link, hw->phy.media_type, + adapter->pf_adapter->priv_flags); + + /* ethtool show all support fiber type when media is unknown */ + if (hw->phy.media_type == rnpm_media_type_unknown) { + if (hw->speed == SPEED_10000) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseER_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseER_Full); + if (adapter->pf_adapter->priv_flags & + RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { + ethtool_link_ksettings_add_link_mode( + ks, supported, 1000baseX_Full); + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseX_Full); + ethtool_link_ksettings_add_link_mode( + ks, supported, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode( + ks, supported, 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseKX_Full); + } + } else { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseKX_Full); + } + /* when media type is unknown, return directly */ + return; + } + + if (phy_type == PHY_TYPE_SGMII) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10baseT_Half); + + rnpm_set_autoneg_adv_from_hw(hw, ks); + } + + if (rnpm_fw_is_old_ethtool(hw) && + (supported_link & RNPM_LINK_SPEED_40GB_FULL)) { + supported_link |= RNPM_SFP_MODE_40G_CR4 | + RNPM_SFP_MODE_40G_SR4 | PHY_TYPE_40G_BASE_LR4; + } + + if (supported_link & RNPM_SFP_MODE_40G_CR4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseCR4_Full); + } + if (supported_link & RNPM_SFP_MODE_40G_SR4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseSR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseSR4_Full); + } + if (supported_link & RNPM_SFP_MODE_40G_LR4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseLR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseLR4_Full); + } + + if (hw->is_backplane) { + if (phy_type == RNPM_LINK_SPEED_40GB_FULL) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseKR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseKR4_Full); + } + if (phy_type == PHY_TYPE_10G_BASE_KR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKR_Full); + if (supported_link & RNPM_LINK_SPEED_10GB_FULL) + ethtool_link_ksettings_add_link_mode( + ks, advertising, 10000baseKR_Full); + } + } + + if (phy_type == PHY_TYPE_1G_BASE_KX) { + if (hw->is_backplane) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); + if (supported_link & RNPM_LINK_SPEED_1GB_FULL) + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseKX_Full); + } else if (supported_link & RNPM_SFP_MODE_1G_T) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + if (supported_link & RNPM_LINK_SPEED_1GB_FULL) + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseT_Full); + } else { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + if (supported_link & RNPM_LINK_SPEED_1GB_FULL) + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseX_Full); + } + } + + /* need to add new 10G PHY types */ + if (phy_type == PHY_TYPE_10G_BASE_SR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseSR_Full); + if (supported_link & RNPM_LINK_SPEED_10GB_FULL) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseSR_Full); + } + if (phy_type == PHY_TYPE_10G_BASE_ER) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseER_Full); + if (supported_link & RNPM_LINK_SPEED_10GB_FULL) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseER_Full); + } + if (phy_type == PHY_TYPE_10G_BASE_LR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseLR_Full); + if (supported_link & RNPM_LINK_SPEED_10GB_FULL) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseLR_Full); + } + + if (phy_type == PHY_TYPE_10G_BASE_SR || + phy_type == PHY_TYPE_10G_BASE_ER || + phy_type == PHY_TYPE_10G_BASE_LR) { + if ((hw->speed == SPEED_1000) || + (supported_link & RNPM_LINK_SPEED_1GB_FULL)) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + if (supported_link & RNPM_LINK_SPEED_10GB_FULL) + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseX_Full); + } + } +} + +/** + * rnpm_get_settings_link_up - Get Link settings for when link is up + * @hw: hw structure + * @ks: ethtool ksettings to fill in + * @netdev: network interface device structure + **/ +static void rnpm_get_settings_link_up(struct rnpm_hw *hw, + struct ethtool_link_ksettings *ks, + struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct ethtool_link_ksettings cap_ksettings; + u32 supported_link = hw->supported_link; + + /* Initialize supported and advertised settings based on phy settings */ + switch (hw->phy_type) { + case PHY_TYPE_40G_BASE_CR4: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseCR4_Full); + break; + + case PHY_TYPE_40G_BASE_SR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseSR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseSR4_Full); + break; + case PHY_TYPE_40G_BASE_LR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseLR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseLR4_Full); + break; + case PHY_TYPE_10G_BASE_SR: + case PHY_TYPE_10G_BASE_LR: + case PHY_TYPE_10G_BASE_ER: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseER_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseER_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + if (hw->speed == SPEED_10000) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); + + if ((hw->speed == SPEED_1000) || + (supported_link & RNPM_LINK_SPEED_1GB_FULL)) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + } + break; + + case PHY_TYPE_1G_BASE_KX: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + if (!!hw->is_backplane) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseKX_Full); + } + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + break; + + case PHY_TYPE_SGMII: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Half); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10baseT_Half); + break; + + case PHY_TYPE_40G_BASE_KR4: + case PHY_TYPE_10G_BASE_KR: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseKR4_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKX4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseKR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKX4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseKX_Full); + break; + + default: + /* if we got here and link is up something bad is afoot */ + netdev_info(netdev, + "WARNING: Link is up but PHY type 0x%x is not ", + hw->phy_type); + netdev_info(netdev, + "recognized, or incorrect cable is in use\n"); + } + + /* Now that we've worked out everything that could be supported by the + * current PHY type, get what is supported by the NVM and intersect + * them to get what is truly supported + */ + memset(&cap_ksettings, 0, sizeof(struct ethtool_link_ksettings)); + rnpm_phy_type_to_ethtool(adapter, &cap_ksettings); + ethtool_intersect_link_masks(ks, &cap_ksettings); + + /* Set speed and duplex */ + ks->base.speed = adapter->speed; + ks->base.duplex = hw->duplex; +} + +/** + * rnpm_get_settings_link_down - Get the Link settings when link is down + * @hw: hw structure + * @ks: ethtool ksettings to fill in + * @netdev: network interface device structure + * + * Reports link settings that can be determined when link is down + **/ +static void rnpm_get_settings_link_down(struct rnpm_hw *hw, + struct ethtool_link_ksettings *ks, + struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + /* link is down and the driver needs to fall back on + * supported phy types to figure out what info to display + */ + rnpm_phy_type_to_ethtool(adapter, ks); + + /* With no link speed and duplex are unknown */ + ks->base.speed = SPEED_UNKNOWN; + ks->base.duplex = hw->duplex; +} + +/** + * rnpm_set_autoneg_state_from_hw - Set the autoneg state from hardware + * @hw: hw structure + * @ks: ethtool ksettings to fill in + * + * Set the autoneg state from hardware, like PHY + **/ +static int rnpm_set_autoneg_state_from_hw(struct rnpm_hw *hw, + struct ethtool_link_ksettings *ks) +{ + struct rnpm_adapter *adapter = hw->back; + + ks->base.autoneg = (adapter->an ? AUTONEG_ENABLE : AUTONEG_DISABLE); + + /* Read autoneg state from phy */ + if (hw->phy_type == PHY_TYPE_SGMII) + ks->base.autoneg = hw->phy.an; + + return 0; +} + +__maybe_unused static int rnpm_get_phy_mdix_from_hw(struct rnpm_hw *hw) +{ + return 0; +} +__maybe_unused static bool fiber_unsupport(u32 supported_link, u8 phy_type) +{ + if ((phy_type == PHY_TYPE_10G_BASE_KR) || + (phy_type == PHY_TYPE_10G_BASE_SR) || + (phy_type == PHY_TYPE_10G_BASE_LR) || + (phy_type == PHY_TYPE_10G_BASE_ER)) { + if (!(supported_link & RNPM_LINK_SPEED_10GB_FULL)) + return true; + } + + if ((phy_type == PHY_TYPE_40G_BASE_KR4) || + (phy_type == PHY_TYPE_40G_BASE_SR4) || + (phy_type == PHY_TYPE_40G_BASE_CR4) || + (phy_type == PHY_TYPE_40G_BASE_LR4)) { + if (!(supported_link & RNPM_LINK_SPEED_40GB_FULL)) + return true; + } + + if (phy_type == PHY_TYPE_1G_BASE_KX) { + if (!(supported_link & RNPM_LINK_SPEED_1GB_FULL)) + return true; + } + + return false; +} + +static bool rnpm_is_unknown_media(struct rnpm_hw *hw) +{ + return false; +} + +static void rnpm_redefine_phy_type(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + + if (adapter->pf_adapter->priv_flags & + RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { + // if (hw->phy_type == PHY_TYPE_1G_BASE_KX) { + if ((hw->speed == SPEED_1000) || + ((hw->phy_type == PHY_TYPE_1G_BASE_KX) || + (hw->phy_type == PHY_TYPE_SGMII))) { + if (hw->supported_link & RNPM_LINK_SPEED_10GB_FULL) { + if (hw->supported_link & RNPM_SFP_MODE_10G_LR) + hw->phy_type = PHY_TYPE_10G_BASE_LR; + if (hw->supported_link & RNPM_SFP_MODE_10G_SR) + hw->phy_type = PHY_TYPE_10G_BASE_SR; + if (hw->supported_link & RNPM_SFP_MODE_10G_LRM) + hw->phy_type = PHY_TYPE_10G_BASE_LR; + if (hw->supported_link & + RNPM_SFP_MODE_10G_BASE_T) + hw->phy_type = PHY_TYPE_10G_BASE_KR; + } + } else { + // if (hw->speed == SPEED_1000) + // hw->phy_type = PHY_TYPE_1G_BASE_KX; + } + } +} + +static void rnpm_get_media_type(struct rnpm_hw *hw) +{ + switch (hw->phy_type) { + case PHY_TYPE_NONE: + hw->phy.media_type = rnpm_media_type_unknown; + break; + case PHY_TYPE_1G_BASE_KX: + if (hw->is_backplane) + hw->phy.media_type = rnpm_media_type_backplane; + else if (hw->is_sgmii) + hw->phy.media_type = rnpm_media_type_copper; + else { + if ((hw->supported_link & RNPM_LINK_SPEED_1GB_FULL) || + (hw->supported_link & RNPM_SFP_MODE_1G_LX)) + hw->phy.media_type = rnpm_media_type_fiber; + else + hw->phy.media_type = rnpm_media_type_unknown; + } + break; + case PHY_TYPE_SGMII: + hw->phy.media_type = rnpm_media_type_copper; + // ks->base.phy_address = adapter->phy_addr; + break; + case PHY_TYPE_10G_BASE_KR: + case PHY_TYPE_25G_BASE_KR: + case PHY_TYPE_40G_BASE_KR4: + hw->phy.media_type = rnpm_media_type_backplane; + break; + case PHY_TYPE_10G_BASE_SR: + case PHY_TYPE_40G_BASE_SR4: + case PHY_TYPE_40G_BASE_CR4: + case PHY_TYPE_40G_BASE_LR4: + case PHY_TYPE_10G_BASE_LR: + case PHY_TYPE_10G_BASE_ER: + hw->phy.media_type = rnpm_media_type_fiber; + break; + default: + hw->phy.media_type = rnpm_media_type_unknown; + break; + } + + if (hw->supported_link & RNPM_SFP_CONNECTOR_DAC) + hw->phy.media_type = rnpm_media_type_da; + + if ((hw->supported_link & RNPM_SFP_TO_SGMII) || + (hw->supported_link & RNPM_SFP_MODE_1G_T)) { + hw->phy.media_type = rnpm_media_type_copper; + } +} + +/** + * rnpm_get_link_ksettings - Get Link Speed and Duplex settings + * @netdev: network interface device structure + * @ks: ethtool ksettings + * + * Reports speed/duplex settings based on media_type + **/ +static int rnpm_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ks) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + bool link_up; + + if (test_bit(__RNPM_REMOVING, &adapter->pf_adapter->state)) + return -1; + + ethtool_link_ksettings_zero_link_mode(ks, supported); + ethtool_link_ksettings_zero_link_mode(ks, advertising); + /* update hw from firmware */ + + if (test_bit(__RNPM_DOWN, &adapter->pf_adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->pf_adapter->state)) + return -1; + + /* when turn on auto speed, the phy_type equal 1G is unreliable */ + rnpm_redefine_phy_type(adapter); + /* update hw->phy.media_type by hw->phy_type */ + rnpm_get_media_type(hw); + + if (hw->phy_type == PHY_TYPE_SGMII) + ks->base.phy_address = adapter->phy_addr; + /* Check Whether there is media on port */ + if (hw->phy.media_type == rnpm_media_type_fiber) { + /* If adapter->sfp.mod_abs is 0, there is no media on port. */ + if (!adapter->sfp.mod_abs) { + hw->phy.media_type = rnpm_media_type_unknown; + rnpm_logd(LOG_ETHTOOL, + "%s absent, set media type is unknown\n", + adapter->netdev->name); + } + } + + if (rnpm_is_unknown_media(hw)) + hw->phy.media_type = rnpm_media_type_unknown; + + /* Now set the settings that don't rely on link being up/down */ + /* Set autoneg settings */ + rnpm_set_autoneg_state_from_hw(hw, ks); + + link_up = hw->link; + if (link_up) + rnpm_get_settings_link_up(hw, ks, netdev); + else + rnpm_get_settings_link_down(hw, ks, netdev); + + /* Set media type settings */ + switch (hw->phy.media_type) { + case rnpm_media_type_backplane: + ethtool_link_ksettings_add_link_mode(ks, supported, Backplane); + ethtool_link_ksettings_add_link_mode(ks, advertising, + Backplane); + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ks->base.port = PORT_NONE; + break; + case rnpm_media_type_copper: + ethtool_link_ksettings_add_link_mode(ks, supported, TP); + ethtool_link_ksettings_add_link_mode(ks, advertising, TP); + if (hw->phy_type == PHY_TYPE_SGMII) + ethtool_link_ksettings_add_link_mode(ks, supported, + Autoneg); + if (ks->base.autoneg == AUTONEG_ENABLE) + ethtool_link_ksettings_add_link_mode(ks, advertising, + Autoneg); + else + ethtool_link_ksettings_del_link_mode(ks, advertising, + Autoneg); + ks->base.port = PORT_TP; + break; + case rnpm_media_type_da: + case rnpm_media_type_cx4: + ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); + ks->base.port = PORT_DA; + break; + case rnpm_media_type_fiber: + ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); + ks->base.port = PORT_FIBRE; + break; + case rnpm_media_type_unknown: + default: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ks->base.port = PORT_OTHER; + break; + } + + /* Set flow control settings */ + ethtool_link_ksettings_add_link_mode(ks, supported, Pause); + ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause); + + switch (hw->fc.requested_mode) { + case rnpm_fc_full: + ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); + break; + case rnpm_fc_tx_pause: + ethtool_link_ksettings_add_link_mode(ks, advertising, + Asym_Pause); + break; + case rnpm_fc_rx_pause: + ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); + ethtool_link_ksettings_add_link_mode(ks, advertising, + Asym_Pause); + break; + default: + ethtool_link_ksettings_del_link_mode(ks, advertising, Pause); + ethtool_link_ksettings_del_link_mode(ks, advertising, + Asym_Pause); + break; + } +#ifdef ETH_TP_MDI_X + /* MDI-X => 2; MDI =>1; Invalid =>0 */ + if (hw->phy_type == PHY_TYPE_SGMII) { + if (rnpm_get_phy_mdix_from_hw(hw) < 0) { + ks->base.eth_tp_mdix = ETH_TP_MDI_INVALID; + } else { + ks->base.eth_tp_mdix = + hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI; + } + } + +#ifdef ETH_TP_MDI_AUTO + if (hw->phy.mdix == AUTO_ALL_MODES) + ks->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + else + ks->base.eth_tp_mdix_ctrl = hw->phy.mdix; + +#endif +#endif /* ETH_TP_MDI_X */ + rnpm_logd(LOG_ETHTOOL, + "%s %s set link: speed=%d port=%d duplex=%d autoneg=%d ", + __func__, netdev->name, ks->base.speed, ks->base.port, + ks->base.duplex, ks->base.autoneg); + rnpm_logd(LOG_ETHTOOL, "%s phy_address=%d mdix_ctrl=%d\n", __func__, + ks->base.phy_address, ks->base.eth_tp_mdix_ctrl); + return 0; +} + +static int rnpm_wol_exclusion(struct rnpm_adapter *adapter, + struct ethtool_wolinfo *wol) +{ + struct rnpm_hw *hw = &adapter->hw; + int retval = 0; + + // if (hw->pfvfnum) { + // retval = 1; + // wol->supported = 0; + // } + + /* WOL not supported for all devices */ + if (!rnpm_wol_supported(adapter, hw->device_id, + hw->subsystem_device_id)) { + retval = 1; + wol->supported = 0; + } + + return retval; +} + +static void rnpm_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + wol->wolopts = 0; + + /* we now can't wol */ + if (rnpm_wol_exclusion(adapter, wol) || + !device_can_wakeup(&adapter->pdev->dev)) + return; + + /* Only support magic */ + if (RNPM_WOL_GET_SUPPORTED(adapter)) + wol->supported = hw->wol_supported; + else + wol->supported = 0; + + if (RNPM_WOL_GET_STATUS(adapter)) + wol->wolopts |= hw->wol_supported; + // printk("DEBUG: rnpm_get_wol wolopts=0x%x wol=0x%x lane=%d\n", + // wol->wolopts, + // adapter->wol, + // adapter->port); +} + +/** + * rnpm_set_wol - set the WakeOnLAN configuration + * @netdev: the netdev in question + * @wol: the ethtool WoL setting data + **/ +static int rnpm_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + // printk("DEBUG: rnp_set_wol wolopts=0x%x wol_supported=0x%x " + // "fw_wol_support=0x%x hw->wol=0x%x\n", + // wol->wolopts, + // hw->wol_supported, + // RNPM_WOL_GET_SUPPORTED(adapter), + // adapter->wol); + + if (!!wol->wolopts) { + if ((wol->wolopts & (~hw->wol_supported)) || + !RNPM_WOL_GET_SUPPORTED(adapter)) + return -EOPNOTSUPP; + } + + RNPM_WOL_SET_SUPPORTED(adapter); + if (wol->wolopts & WAKE_MAGIC) { + RNPM_WOL_SET_SUPPORTED(adapter); + RNPM_WOL_SET_STATUS(adapter); + } else { + RNPM_WOL_CLEAR_STATUS(adapter); + } + rnpm_mbx_wol_set(hw, RNPM_WOL_GET_STATUS(adapter)); + // printk("DEBUG: set wol=0x%x status=%d\n", + // adapter->wol, + // RNPM_WOL_GET_STATUS(adapter)); + device_set_wakeup_enable(&adapter->pdev->dev, !!wol->wolopts); + + return 0; +} + +/* ethtool register test data */ +struct rnpm_reg_test { + u16 reg; + u8 array_len; + u8 test_type; + u32 mask; + u32 write; +}; + +/* In the hardware, registers are laid out either singly, in arrays + * spaced 0x40 bytes apart, or in contiguous tables. We assume + * most tests take place on arrays or single registers (handled + * as a single-element array) and special-case the tables. + * Table tests are always pattern tests. + * + * We also make provision for some required setup steps by specifying + * registers to be written without any read-back testing. + */ + +#define PATTERN_TEST 1 +#define SET_READ_TEST 2 +#define WRITE_NO_TEST 3 +#define TABLE32_TEST 4 +#define TABLE64_TEST_LO 5 +#define TABLE64_TEST_HI 6 + +/* default n10 register test */ +static struct rnpm_reg_test reg_test_n10[] = { { .reg = 0 } }; + +/* write and read check */ +static bool reg_pattern_test(struct rnpm_adapter *adapter, u64 *data, int reg, + u32 mask, u32 write) +{ + u32 pat, val, before; + static const u32 test_pattern[] = { 0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, + 0xFFFFFFFF }; + + for (pat = 0; pat < ARRAY_SIZE(test_pattern); pat++) { + before = readl(adapter->hw.hw_addr + reg); + writel((test_pattern[pat] & write), + (adapter->hw.hw_addr + reg)); + val = readl(adapter->hw.hw_addr + reg); + if (val != (test_pattern[pat] & write & mask)) { + e_err(drv, + "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n", + reg, val, (test_pattern[pat] & write & mask)); + *data = reg; + writel(before, adapter->hw.hw_addr + reg); + return 1; + } + writel(before, adapter->hw.hw_addr + reg); + } + return 0; +} + +static bool reg_set_and_check(struct rnpm_adapter *adapter, u64 *data, int reg, + u32 mask, u32 write) +{ + u32 val, before; + + before = readl(adapter->hw.hw_addr + reg); + writel((write & mask), (adapter->hw.hw_addr + reg)); + val = readl(adapter->hw.hw_addr + reg); + if ((write & mask) != (val & mask)) { + e_err(drv, + "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", + reg, (val & mask), (write & mask)); + *data = reg; + writel(before, (adapter->hw.hw_addr + reg)); + return 1; + } + writel(before, (adapter->hw.hw_addr + reg)); + return 0; +} + +__maybe_unused static bool rnpm_reg_test(struct rnpm_adapter *adapter, + u64 *data) +{ + struct rnpm_reg_test *test; + struct rnpm_hw *hw = &adapter->hw; + // u32 value, before, after; + u32 i; + + if (RNPM_REMOVED(hw->hw_addr)) { + e_err(drv, "Adapter removed - register test blocked\n"); + *data = 1; + return true; + } + + test = reg_test_n10; + /* Perform the remainder of the register test, looping through + * the test table until we either fail or reach the null entry. + */ + while (test->reg) { + for (i = 0; i < test->array_len; i++) { + bool b = false; + + switch (test->test_type) { + case PATTERN_TEST: + b = reg_pattern_test(adapter, data, + test->reg + (i * 0x40), + test->mask, test->write); + break; + case SET_READ_TEST: + b = reg_set_and_check(adapter, data, + test->reg + (i * 0x40), + test->mask, test->write); + break; + case WRITE_NO_TEST: + wr32(hw, test->reg + (i * 0x40), test->write); + break; + case TABLE32_TEST: + b = reg_pattern_test(adapter, data, + test->reg + (i * 4), + test->mask, test->write); + break; + case TABLE64_TEST_LO: + b = reg_pattern_test(adapter, data, + test->reg + (i * 8), + test->mask, test->write); + break; + case TABLE64_TEST_HI: + b = reg_pattern_test(adapter, data, + (test->reg + 4) + (i * 8), + test->mask, test->write); + break; + } + if (b) + return true; + } + test++; + } + + *data = 0; + return false; +} + +static u64 rnpm_link_test(struct rnpm_adapter *adapter, u64 *data) +{ + struct rnpm_hw *hw = &adapter->hw; + bool link_up = false; + u32 link_speed = 0; + *data = 0; + + hw->mac.ops.check_link(hw, &link_speed, &link_up, true); + if (link_up) + *data = 0; + else + *data = 1; + return *data; +} + +static void rnpm_diag_test(struct net_device *netdev, + struct ethtool_test *eth_test, u64 *data) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + bool if_running = netif_running(netdev); + + set_bit(__RNPM_TESTING, &adapter->state); + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + int i; + + for (i = 0; i < adapter->num_vfs; i++) { + if (adapter->vfinfo[i].clear_to_send) { + netdev_warn( + netdev, "%s", + "offline diagnostic donnot support when VF present\n"); + data[0] = 1; + data[1] = 1; + data[2] = 1; + data[3] = 1; + eth_test->flags |= ETH_TEST_FL_FAILED; + clear_bit(__RNPM_TESTING, + &adapter->state); + goto skip_ol_tests; + } + } + } + + /* Offline tests */ + e_info(hw, "offline testing starting\n"); + /* bringing adapter down disables SFP+ optics */ + if (hw->mac.ops.enable_tx_laser) + hw->mac.ops.enable_tx_laser(hw); + + /* Link test performed before hardware reset so autoneg doesn't + * interfere with test result + */ + if (rnpm_link_test(adapter, &data[4])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + rnpm_reset(adapter); + e_info(hw, "register testing starting\n"); + if (rnpm_reg_test(adapter, &data[0])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + data[1] = 1; + data[2] = 1; + data[3] = 1; + /* If SRIOV or VMDq is enabled then skip MAC + * loopback diagnostic + */ + if (adapter->flags & + (RNPM_FLAG_SRIOV_ENABLED | RNPM_FLAG_VMDQ_ENABLED)) { + netdev_warn( + netdev, + "Skip MAC loopback diagnostic in VT mode\n"); + data[3] = 0; + } + /* clear testing bit and return adapter to previous state */ + clear_bit(__RNPM_TESTING, &adapter->state); + } else { + e_info(hw, "online testing starting\n"); + + /* if adapter is down, SFP+ optics will be disabled */ + if (!if_running && hw->mac.ops.enable_tx_laser) + hw->mac.ops.enable_tx_laser(hw); + + /* Online tests */ + if (rnpm_link_test(adapter, &data[4])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + /* Offline tests aren't run; pass by default */ + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + + clear_bit(__RNPM_TESTING, &adapter->state); + } + + /* if adapter was down, ensure SFP+ optics are disabled again */ + if (!if_running && hw->mac.ops.disable_tx_laser) + hw->mac.ops.disable_tx_laser(hw); +skip_ol_tests: + msleep_interruptible(4 * 1000); +} + +/** + * rnpm_set_link_ksettings - Set Speed and Duplex + * @netdev: network interface device structure + * @ks: ethtool ksettings + * + * Set speed/duplex per media_types advertised/forced + **/ +static int rnpm_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *ks) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct ethtool_link_ksettings safe_ks; + struct ethtool_link_ksettings copy_ks; + bool autoneg_changed = false, duplex_changed = false; + int timeout = 50; + int err = 0; + u8 autoneg; + u32 advertising_link_speed; + + /* copy the ksettings to copy_ks to avoid modifying the origin */ + memcpy(©_ks, ks, sizeof(struct ethtool_link_ksettings)); + /* save autoneg out of ksettings */ + autoneg = copy_ks.base.autoneg; + rnpm_logd(LOG_ETHTOOL, + "%s %s set link: speed=%d port=%d duplex=%d autoneg=%d ", + __func__, netdev->name, copy_ks.base.speed, copy_ks.base.port, + copy_ks.base.duplex, copy_ks.base.autoneg); + rnpm_logd(LOG_ETHTOOL, "phy_address=%d mdix_ctrl=%d\n", + copy_ks.base.phy_address, copy_ks.base.eth_tp_mdix_ctrl); + /* get our own copy of the bits to check against */ + memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings)); + safe_ks.base.cmd = copy_ks.base.cmd; + safe_ks.base.link_mode_masks_nwords = + copy_ks.base.link_mode_masks_nwords; + + if (rnpm_get_link_ksettings(netdev, &safe_ks)) { + /* return err */ + return 0; + } + + if (!adapter->pf_adapter->force_10g_1g_speed_ablity) { + /* Checkout the media_type */ + if (hw->phy.media_type != rnpm_media_type_fiber && + hw->phy.media_type != rnpm_media_type_copper && + hw->phy.media_type != rnpm_media_type_backplane && + hw->phy.media_type != rnpm_media_type_cx4 && + hw->phy.media_type != rnpm_media_type_da) + return -EOPNOTSUPP; + } + + /* Get link modes supported by hardware and check against modes + * requested by user. Return an error if unsupported mode was set. + */ + if (!bitmap_subset(copy_ks.link_modes.advertising, + safe_ks.link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS)) + return -EINVAL; + +#ifdef ETH_TP_MDI_AUTO + /* MDI setting is only allowed when autoneg enabled because + * some hardware doesn't allow MDI setting when speed or + * duplex is forced. + */ + if (copy_ks.base.eth_tp_mdix_ctrl && hw->is_sgmii) { + if (hw->phy.media_type != rnpm_media_type_copper) + return -EOPNOTSUPP; + + if (copy_ks.base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && + copy_ks.base.autoneg != AUTONEG_ENABLE) { + netdev_info( + netdev, + "forcing MDI/MDI-X state is not supported when link\n"); + return -EINVAL; + } + } +#endif /* ETH_TP_MDI_AUTO */ + /* set autoneg back to what it currently is */ + copy_ks.base.autoneg = safe_ks.base.autoneg; + memset(&advertising_link_speed, 0, sizeof(u32)); + + /* Check autoneg */ + if (autoneg == AUTONEG_ENABLE) { + /* If autoneg was not already enabled */ + if (!(adapter->an)) { + /* If autoneg is not supported, return error */ + if (!ethtool_link_ksettings_test_link_mode( + &safe_ks, supported, Autoneg)) { + netdev_info( + netdev, + "Autoneg not supported on this phy\n"); + err = -EINVAL; + + goto done; + } + /* Autoneg is allowed to change */ + autoneg_changed = true; + } + + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 10baseT_Full)) + advertising_link_speed |= RNPM_LINK_SPEED_10_FULL; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 100baseT_Full)) + advertising_link_speed |= RNPM_LINK_SPEED_100_FULL; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseT_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseX_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseKX_Full)) + advertising_link_speed |= RNPM_LINK_SPEED_1GB_FULL; + + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 10baseT_Half)) + advertising_link_speed |= RNPM_LINK_SPEED_10_HALF; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 100baseT_Half)) + advertising_link_speed |= RNPM_LINK_SPEED_100_HALF; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseT_Half)) + advertising_link_speed |= RNPM_LINK_SPEED_1GB_HALF; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseT_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseKX4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseKR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseCR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseSR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseLR_Full)) + advertising_link_speed |= RNPM_LINK_SPEED_10GB_FULL; + + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseKR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseCR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseSR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseLR4_Full)) + advertising_link_speed |= RNPM_LINK_SPEED_40GB_FULL; + + if (advertising_link_speed) { + hw->phy.autoneg_advertised = advertising_link_speed; + } else { + // err = -EINVAL; + // RNPM_LINK_SPEED_UNKNOWN + // goto done; + } + if (hw->is_sgmii && hw->mac.autoneg == false) + autoneg_changed = true; + hw->mac.autoneg = true; + } else { + if (!hw->is_sgmii && + !adapter->pf_adapter->force_10g_1g_speed_ablity) { + err = -EOPNOTSUPP; + goto done; + } + /* If autoneg is currently enabled */ + if (adapter->an) { + /* If autoneg is supported 10GBASE_T is the only PHY + * that can disable it, so otherwise return error + */ + if (ethtool_link_ksettings_test_link_mode( + &safe_ks, supported, Autoneg) && + hw->phy.media_type != rnpm_media_type_copper) { + netdev_info( + netdev, + "Autoneg cannot be disabled on this phy\n"); + err = -EINVAL; + + goto done; + } + /* Autoneg is allowed to change */ + autoneg_changed = true; + } + + /* Only allow one speed at a time when autoneg is AUTONEG_DISABLE. */ + switch (ks->base.speed) { + case SPEED_10: + advertising_link_speed = RNPM_LINK_SPEED_10_FULL; + break; + case SPEED_100: + advertising_link_speed = RNPM_LINK_SPEED_100_FULL; + break; + case SPEED_1000: + advertising_link_speed = RNPM_LINK_SPEED_1GB_FULL; + break; + case SPEED_10000: + advertising_link_speed = RNPM_LINK_SPEED_10GB_FULL; + break; + default: + netdev_info(netdev, "unsupported speed\n"); + err = -EINVAL; + + goto done; + } + + hw->mac.autoneg = false; + } + + hw->phy.autoneg_advertised = RNPM_LINK_SPEED_UNKNOWN; + /* If speed didn't get set, set it to what it currently is. + * This is needed because if advertise is 0 (as it is when autoneg + * is disabled) then speed won't get set. + */ + // old_link_speed = hw->phy.autoneg_advertised; + // if (!advertising_link_speed) + // advertising_link_speed = old_link_speed; + if (hw->is_sgmii) { + // duplex_changed = !!(hw->mac.duplex != ks->base.duplex); + hw->mac.duplex = ks->base.duplex; + duplex_changed = true; + } + + /* If the unsupported speed is set, return -EOPNOTSUPP error. */ + // if ((advertising_link_speed | hw->supported_link) != hw->supported_link) + // return -EOPNOTSUPP; + + // if (autoneg_changed || duplex_changed || + // (hw->phy.autoneg_advertised != advertising_link_speed)) { + /* this sets the link speed and restarts auto-neg */ + while (test_and_set_bit(__RNPM_IN_SFP_INIT, &adapter->state)) { + timeout--; + if (!timeout) + return -EBUSY; + usleep_range(1000, 2000); + } + +#ifdef ETH_TP_MDI_AUTO + /* MDI-X => 2; MDI => 1; Auto => 3 */ + if (copy_ks.base.eth_tp_mdix_ctrl) { + /* fix up the value for auto (3 => 0) as zero is mapped + * internally to auto + */ + if (copy_ks.base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) + hw->phy.mdix = AUTO_ALL_MODES; + else + hw->phy.mdix = copy_ks.base.eth_tp_mdix_ctrl; + } + +#endif /* ETH_TP_MDI_AUTO */ + + hw->mac.autotry_restart = true; + /* set speed */ + err = hw->mac.ops.setup_link(hw, advertising_link_speed, true); + if (err) { + e_info(probe, "setup link failed with code %d\n", err); + // hw->mac.ops.setup_link(hw, old_link_speed, true); + } + clear_bit(__RNPM_IN_SFP_INIT, &adapter->state); + //} + +done: + return err; +} + +/** + * rnpm_get_pauseparam - Get Flow Control status + * @netdev: netdevice structure + * @pause: buffer to return pause parameters + * + * Return tx/rx-pause status + **/ +static void rnpm_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + rnpm_redefine_phy_type(adapter); + rnpm_get_media_type(hw); + + if (rnpm_device_supports_autoneg_fc(hw) && !hw->fc.disable_fc_autoneg) + pause->autoneg = 1; + else + pause->autoneg = 0; + + if (hw->fc.current_mode == rnpm_fc_rx_pause) { + pause->rx_pause = 1; + } else if (hw->fc.current_mode == rnpm_fc_tx_pause) { + pause->tx_pause = 1; + } else if (hw->fc.current_mode == rnpm_fc_full) { + pause->rx_pause = 1; + pause->tx_pause = 1; + } +} + +/** + * rnpm_set_pauseparam - Set Flow Control parameter + * @netdev: network interface device structure + * @pause: return tx/rx flow control status + **/ +static int rnpm_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_fc_info fc = hw->fc; + + /* we not support change in dcb mode */ + if (adapter->flags & RNPM_FLAG_DCB_ENABLED) + return -EINVAL; + rnpm_redefine_phy_type(adapter); + rnpm_get_media_type(hw); + + /* some devices do not support autoneg of flow control */ + if ((pause->autoneg == AUTONEG_ENABLE) && + !rnpm_device_supports_autoneg_fc(hw)) + return -EINVAL; + + fc.disable_fc_autoneg = (pause->autoneg != AUTONEG_ENABLE); + + if ((pause->rx_pause && pause->tx_pause) || (pause->autoneg)) + fc.requested_mode = rnpm_fc_full; + else if (pause->rx_pause) + fc.requested_mode = rnpm_fc_rx_pause; + else if (pause->tx_pause) + fc.requested_mode = rnpm_fc_tx_pause; + else + fc.requested_mode = rnpm_fc_none; + + /* if the thing changed then we'll update and use new autoneg */ + if (memcmp(&fc, &hw->fc, sizeof(struct rnpm_fc_info))) { + hw->fc = fc; + /* to tell all vf new pause status */ + // rnpm_msg_post_status(adapter, PF_PAUSE_STATUS); + if (netif_running(netdev)) + rnpm_reinit_locked(adapter); + else + rnpm_reset(adapter); + } + + return 0; +} + +static int rnpm_get_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + int err; + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + err = rnpm_mbx_get_lane_stat(hw); + if (err) + return err; + + if (adapter->fec) + fecparam->active_fec = ETHTOOL_FEC_BASER; + else + fecparam->active_fec = ETHTOOL_FEC_NONE; + fecparam->fec = ETHTOOL_FEC_BASER; + + return 0; +} + +static int rnpm_set_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + // int err; + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (fecparam->fec & ETHTOOL_FEC_OFF) + return rnpm_set_lane_fun(hw, LANE_FUN_FEC, 0, 0, 0, 0); + else if (fecparam->fec & ETHTOOL_FEC_BASER) + return rnpm_set_lane_fun(hw, LANE_FUN_FEC, 1, 0, 0, 0); + + return -EINVAL; +} + +static u32 rnpm_get_msglevel(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + return adapter->msg_enable; +} + +static void rnpm_set_msglevel(struct net_device *netdev, u32 data) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + adapter->msg_enable = data; +} + +static int rnpm_set_phys_id(struct net_device *netdev, + enum ethtool_phys_id_state state) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + rnpm_mbx_led_set(hw, 1); + return 2; /*twic peer seconds*/ + case ETHTOOL_ID_ON: + rnpm_mbx_led_set(hw, 2); + break; + case ETHTOOL_ID_OFF: + rnpm_mbx_led_set(hw, 3); + break; + case ETHTOOL_ID_INACTIVE: + rnpm_mbx_led_set(hw, 0); + break; + default: + return -ENOENT; + } + + return 0; +} + +static int rnpm_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); +#ifndef NO_PTP + /*For we just set it as pf0 */ + if (!(adapter->flags2 & RNPM_FLAG2_PTP_ENABLED)) + return ethtool_op_get_ts_info(dev, info); + if (adapter->ptp_clock) + info->phc_index = ptp_clock_index(adapter->ptp_clock); + else + info->phc_index = -1; + + ptp_dbg("phc_index is %d\n", info->phc_index); + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RAW_HARDWARE; + + info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); + + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_ALL); +#ifdef PTP_802_AS1 + /* 802.AS1 */ + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ); +#endif + +#else + info->phc_index = -1; + +#endif + return 0; +} + +static unsigned int rnpm_max_channels(struct rnpm_adapter *adapter) +{ + unsigned int max_combined; + + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + /* SR-IOV currently only allows 2 queue on the PF */ + max_combined = PF_RING_CNT_WHEN_IOV_ENABLED; + } else { + /* support up to 16 queues with RSS */ + max_combined = adapter->max_ring_pair_counts; + /* should not large than q_vectors ? */ + // if dcb is off + // max_combined = adapter->num_q_vectors; + } + + return max_combined; +} + +/** + * rnpm_get_channels - Get the current channels enabled and max supported etc. + * @dev: network interface device structure + * @ch: ethtool channels structure + * + * We don't support separate tx and rx queues as channels. The other count + * represents how many queues are being used for control. max_combined counts + * how many queue pairs we can support. They may not be mapped 1 to 1 with + * q_vectors since we support a lot more queue pairs than q_vectors. + **/ +static void rnpm_get_channels(struct net_device *dev, + struct ethtool_channels *ch) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + + /* report maximum channels */ + ch->max_combined = rnpm_max_channels(adapter); + + /* report info for other vector */ + ch->max_other = NON_Q_VECTORS; + ch->other_count = NON_Q_VECTORS; + + /* record RSS queues */ + ch->combined_count = adapter->ring_feature[RING_F_RSS].indices; + + /* nothing else to report if RSS is disabled */ + if (ch->combined_count == 1) + return; + + /* we do not support ATR queueing if SR-IOV is enabled */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + return; + + /* same thing goes for being DCB enabled */ + if (netdev_get_num_tc(dev) > 1) + return; + + /* report flow director queues as maximum channels */ + // ch->combined_count = adapter->ring_feature[RING_F_FDIR].indices; +} + +/** + * rnpm_set_channels - Set the new channels count. + * @dev: network interface device structure + * @ch: ethtool channels structure + * + * The new channels count may not be the same as requested by the user + * since it gets rounded down to a power of 2 value. + **/ +static int rnpm_set_channels(struct net_device *dev, + struct ethtool_channels *ch) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + unsigned int count = ch->combined_count; + + /* verify they are not requesting separate vectors */ + if (!count || ch->rx_count || ch->tx_count) + return -EINVAL; + + /* verify other_count has not changed */ + if (ch->other_count != NON_Q_VECTORS) + return -EINVAL; + + /* verify the number of channels does not exceed hardware limits */ + if (count > rnpm_max_channels(adapter)) + return -EINVAL; + + /* update feature limits from largest to smallest supported values */ + adapter->ring_feature[RING_F_FDIR].limit = count; + + if (count > adapter->max_ring_pair_counts) + count = adapter->max_ring_pair_counts; + /* use this to limit ring num */ + adapter->ring_feature[RING_F_RSS].limit = count; + + /* use setup TC to update any traffic class queue mapping */ + return rnpm_setup_tc(dev, netdev_get_num_tc(dev)); +} + +/** + * rnpm_get_module_info - get (Q)SFP+ module type info + * @netdev: network interface device structure + * @modinfo: module EEPROM size and layout information structure + **/ +static int rnpm_get_module_info(struct net_device *dev, + struct ethtool_modinfo *modinfo) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + struct rnpm_hw *hw = &adapter->hw; + u8 module_id, diag_supported; + int rc; + + rnpm_mbx_get_lane_stat(hw); + if (hw->is_sgmii) + return -EIO; + + /* Check if firmware supports reading module EEPROM. */ + rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, SFF_MODULE_ID_OFFSET, 1, + &module_id); + if (rc || module_id == 0xff) + return -EIO; + + rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, SFF_DIAG_SUPPORT_OFFSET, + 1, &diag_supported); + if (!rc) { + switch (module_id) { + case SFF_MODULE_ID_SFF: + case SFF_MODULE_ID_SFP: + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + if (!diag_supported) + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + case SFF_MODULE_ID_QSFP: + case SFF_MODULE_ID_QSFP_PLUS: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + case SFF_MODULE_ID_QSFP28: + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = RNPM_MODULE_QSFP_MAX_LEN; + break; + default: + netdev_err( + dev, + "SFP module type unrecognized or no SFP connector.\n"); + return -EINVAL; + } + } + + return 0; +} + +/** + * rnpm_get_module_eeprom - fills buffer with (Q)SFP+ module memory contents + * @netdev: network interface device structure + * @ee: EEPROM dump request structure + * @data: buffer to be filled with EEPROM contents + **/ +static int rnpm_get_module_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + struct rnpm_hw *hw = &adapter->hw; + u16 start = eeprom->offset, length = eeprom->len; + int rc = 0; + + memset(data, 0, eeprom->len); + + /* Read A0 portion of the EEPROM */ + if (start < ETH_MODULE_SFF_8436_LEN) { + if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN) + length = ETH_MODULE_SFF_8436_LEN - start; + rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, start, length, + data); + if (rc) + return rc; + start += length; + data += length; + length = eeprom->len - length; + } + + /* Read A2 portion of the EEPROM */ + if (length) { + start -= ETH_MODULE_SFF_8436_LEN; + rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA2, start, length, + data); + } + + return rc; +} + +static void +rnpm_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam __always_unused *ker, + struct netlink_ext_ack __always_unused *extack) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + /* all ring share the same status*/ + ring->rx_max_pending = RNPM_MAX_RXD; + ring->tx_max_pending = RNPM_MAX_TXD; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = adapter->rx_ring_item_count; + ring->tx_pending = adapter->tx_ring_item_count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static int +rnpm_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam __always_unused *ker, + struct netlink_ext_ack __always_unused *extack) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_ring *temp_ring; + int i, err = 0; + u32 new_rx_count, new_tx_count; + + if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) + return -EINVAL; + if ((ring->tx_pending < RNPM_MIN_TXD) || + (ring->tx_pending > RNPM_MAX_TXD) || + (ring->rx_pending < RNPM_MIN_RXD) || + (ring->rx_pending > RNPM_MAX_RXD)) { + netdev_info( + netdev, + "Descriptors requested (Tx: %d / Rx: %d) out of range [%d-%d]\n", + ring->tx_pending, ring->rx_pending, RNPM_MIN_TXD, + RNPM_MAX_TXD); + return -EINVAL; + } + + new_tx_count = + clamp_t(u32, ring->tx_pending, RNPM_MIN_TXD, RNPM_MAX_TXD); + new_tx_count = ALIGN(new_tx_count, RNPM_REQ_TX_DESCRIPTOR_MULTIPLE); + + new_rx_count = + clamp_t(u32, ring->rx_pending, RNPM_MIN_RXD, RNPM_MAX_RXD); + new_rx_count = ALIGN(new_rx_count, RNPM_REQ_RX_DESCRIPTOR_MULTIPLE); + + if ((new_tx_count == adapter->tx_ring_item_count) && + (new_rx_count == adapter->rx_ring_item_count)) { + /* nothing to do */ + return 0; + } + + while (test_and_set_bit(__RNPM_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (!netif_running(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) + adapter->tx_ring[i]->count = new_tx_count; + for (i = 0; i < adapter->num_rx_queues; i++) + adapter->rx_ring[i]->count = new_rx_count; + adapter->tx_ring_item_count = new_tx_count; + adapter->rx_ring_item_count = new_rx_count; + goto clear_reset; + } + + /* allocate temporary buffer to store rings in */ + i = max_t(int, adapter->num_tx_queues, adapter->num_rx_queues); + temp_ring = vmalloc(i * sizeof(struct rnpm_ring)); + if (!temp_ring) { + err = -ENOMEM; + goto clear_reset; + } + memset(temp_ring, 0x00, i * sizeof(struct rnpm_ring)); + + if (new_rx_count != adapter->rx_ring_item_count) { + for (i = 0; i < adapter->num_rx_queues; i++) { + struct rnpm_ring *ring = adapter->rx_ring[i]; + + ring->reset_count = new_rx_count; + ring->ring_flags |= RNPM_RING_FLAG_CHANGE_RX_LEN; + } + } + rnpm_down(adapter); + /* Setup new Tx resources and free the old Tx resources in that order. + * We can then assign the new resources to the rings via a memcpy. + * The advantage to this approach is that we are guaranteed to still + * have resources even in the case of an allocation failure. + */ + if (new_tx_count != adapter->tx_ring_item_count) { + netdev_info(netdev, + "Changing Tx descriptor count from %d to %d\n", + adapter->tx_ring_item_count, new_tx_count); + for (i = 0; i < adapter->num_tx_queues; i++) { + memcpy(&temp_ring[i], adapter->tx_ring[i], + sizeof(struct rnpm_ring)); + + temp_ring[i].count = new_tx_count; + err = rnpm_setup_tx_resources(&temp_ring[i], adapter); + if (err) { + while (i) { + i--; + rnpm_free_tx_resources(&temp_ring[i]); + } + goto err_setup; + } + } + + for (i = 0; i < adapter->num_tx_queues; i++) { + rnpm_free_tx_resources(adapter->tx_ring[i]); + memcpy(adapter->tx_ring[i], &temp_ring[i], + sizeof(struct rnpm_ring)); + } + + adapter->tx_ring_item_count = new_tx_count; + } + + /* Repeat the process for the Rx rings if needed */ + if (new_rx_count != adapter->rx_ring_item_count) { + netdev_info(netdev, + "Changing Rx descriptor count from %d to %d\n", + adapter->rx_ring_item_count, new_rx_count); + for (i = 0; i < adapter->num_rx_queues; i++) { + memcpy(&temp_ring[i], adapter->rx_ring[i], + sizeof(struct rnpm_ring)); + /* setup ring count */ + if (!(adapter->rx_ring[i]->ring_flags & + RNPM_RING_FLAG_DELAY_SETUP_RX_LEN)) { + temp_ring[i].count = new_rx_count; + } else { + /* setup temp count */ + temp_ring[i].count = temp_ring[i].temp_count; + adapter->rx_ring[i]->reset_count = new_rx_count; + } + err = rnpm_setup_rx_resources(&temp_ring[i], adapter); + if (err) { + while (i) { + i--; + rnpm_free_rx_resources(&temp_ring[i]); + } + goto err_setup; + } + } + + for (i = 0; i < adapter->num_rx_queues; i++) { + rnpm_free_rx_resources(adapter->rx_ring[i]); + memcpy(adapter->rx_ring[i], &temp_ring[i], + sizeof(struct rnpm_ring)); + } + adapter->rx_ring_item_count = new_rx_count; + } + +err_setup: + rnpm_up(adapter); + vfree(temp_ring); +clear_reset: + clear_bit(__RNPM_RESETTING, &adapter->state); + return err; +} + +static void rnpm_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + char *p = (char *)data; + int i; + struct rnpm_ring *ring; + u32 dma_ch; + + switch (stringset) { + /* maybe we don't support test? */ + case ETH_SS_TEST: + for (i = 0; i < RNPM_TEST_LEN; i++) { + memcpy(data, rnpm_gstrings_test[i], ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; + case ETH_SS_STATS: + for (i = 0; i < RNPM_GLOBAL_STATS_LEN; i++) { + memcpy(p, rnpm_gstrings_net_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < RNPM_HWSTRINGS_STATS_LEN; i++) { + memcpy(p, rnpm_hwstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < RNPM_NUM_TX_QUEUES; i++) { +#define SHORT_STATS + +#ifdef SHORT_STATS + //==== tx ======== + ring = adapter->tx_ring[i]; + dma_ch = ring->rnpm_queue_idx; + sprintf(p, "---\n queue%u_tx_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_bytes", i); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_tx_restart", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_busy", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_done_old", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_clean_desc", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_poll_count", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_irq_more", i); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_tx_hw_head", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_hw_tail", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_sw_next_to_clean", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_sw_next_to_use", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_send_bytes", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_send_bytes_to_hw", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_todo_update", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_send_done_bytes", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_added_vlan_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_next_to_clean", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_irq_miss", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_tx_equal_count", i); + p += ETH_GSTRING_LEN; + //==== rx ======== + ring = adapter->rx_ring[i]; + dma_ch = ring->rnpm_queue_idx; + sprintf(p, "queue%u_rx_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_bytes", i); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_rx_driver_dropped_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_rsc", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_rsc_flush", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_non_eop_descs", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_alloc_page_failed", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_alloc_buff_failed", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_csum_offload_errs", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_csum_offload_good", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_poll_again_count", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_rm_vlan_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_alloc_rx_page", i); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_rx_hw_head", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_hw_tail", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_sw_next_to_use", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_sw_next_to_clean", i); + /* dbg desc */ + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_next_to_clean", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_irq_miss", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_equal_count", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_poll_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_poll_avg_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_rx_poll_itr", i); + p += ETH_GSTRING_LEN; +#else + //==== tx ======== + ring = adapter->tx_ring[i]; + dma_ch = ring->rnpm_queue_idx; + sprintf(p, "queue%u_dma%u_tx_packets", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_bytes", i, dma_ch); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_dma%u_tx_restart", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_busy", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_done_old", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_clean_desc", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_poll_count", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_irq_more", i, dma_ch); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_dma%u_tx_hw_head", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_hw_tail", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_sw_next_to_clean", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_sw_next_to_use", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_send_bytes", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_send_bytes_to_hw", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_todo_update", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_send_done_bytes", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_added_vlan_packets", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_next_to_clean", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_irq_miss", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_tx_equal_count", i, dma_ch); + p += ETH_GSTRING_LEN; + //==== rx ======== + ring = adapter->rx_ring[i]; + dma_ch = ring->rnpm_queue_idx; + sprintf(p, "queue%u_dma%u_rx_packets", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_bytes", i, dma_ch); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_dma%u_rx_driver_drop_packets", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_rsc", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_rsc_flush", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_non_eop_descs", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_alloc_page_failed", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_alloc_buff_failed", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_csum_offload_errs", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_csum_offload_good", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_poll_again_count", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_rm_vlan_packets", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_alloc_rx_page", i, dma_ch); + p += ETH_GSTRING_LEN; + + sprintf(p, "queue%u_dma%u_rx_hw_head", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_hw_tail", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_sw_next_to_use", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_sw_next_to_clean", i, + dma_ch); + /* dbg desc */ + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_next_to_clean", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_irq_miss", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_equal_count", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_poll_packets", i, dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_poll_avg_packets", i, + dma_ch); + p += ETH_GSTRING_LEN; + sprintf(p, "queue%u_dma%u_rx_poll_itr", i, dma_ch); + p += ETH_GSTRING_LEN; +#endif /* SHORT_STATS */ + } + + break; + case ETH_SS_PRIV_FLAGS: + memcpy(data, rnpm_priv_flags_strings, + RNPM_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); + break; + case ETH_SS_PHY_STATS: + memcpy(data, rnpm_phy_statistics_strings, + RNPM_PHY_STATISTICS_STR_LEN * ETH_GSTRING_LEN); + break; + } +} + +__maybe_unused static int rnpm_get_dump_flag(struct net_device *netdev, + struct ethtool_dump *dump) +{ + struct rnpm_adapter *adapter = + (struct rnpm_adapter *)netdev_priv(netdev); + // struct rnpm_hw *hw = &adapter->hw; + // struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + + rnpm_mbx_get_dump(&adapter->hw, 0, NULL, 0); + + dump->flag = adapter->hw.dump.flag; + dump->len = adapter->hw.dump.len; + dump->version = adapter->hw.dump.version; + + return 0; +} + +__maybe_unused static int rnpm_get_dump_data(struct net_device *netdev, + struct ethtool_dump *dump, + void *buffer) +{ + int err; + struct rnpm_adapter *adapter = netdev_priv(netdev); + + err = rnpm_mbx_get_dump(&adapter->hw, dump->flag, buffer, dump->len); + if (err) + return err; + + dump->flag = adapter->hw.dump.flag; + dump->len = adapter->hw.dump.len; + dump->version = adapter->hw.dump.version; + + return 0; +} + +__maybe_unused static int rnpm_set_dump(struct net_device *netdev, + struct ethtool_dump *dump) +{ + // int err; + struct rnpm_adapter *adapter = netdev_priv(netdev); + + rnpm_mbx_set_dump(&adapter->hw, dump->flag); + + return 0; +} + +static int rnpm_get_sset_count(struct net_device *netdev, int sset) +{ +#ifdef NO_REAL_QUEUE_NUM + struct rnpm_adapter *adapter = + (struct rnpm_adapter *)netdev_priv(netdev); +#endif + + switch (sset) { + /* now we don't support test */ + case ETH_SS_TEST: + return RNPM_TEST_LEN; + case ETH_SS_STATS: + return RNPM_STATS_LEN; + case ETH_SS_PRIV_FLAGS: + return RNPM_PRIV_FLAGS_STR_LEN; + case ETH_SS_PHY_STATS: + return RNPM_PHY_STATISTICS_STR_LEN; + default: + return -EOPNOTSUPP; + } +} + +/** + * rnpm_get_priv_flags - report device private flags + * @dev: network interface device structure + * + * The get string set count and the string set should be matched for each + * flag returned. Add new strings for each flag to the rnpm_gstrings_priv_flags + * array. + * + * Returns a u32 bitmap of flags. + **/ +static u32 rnpm_get_priv_flags(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = + (struct rnpm_adapter *)netdev_priv(netdev); + // struct rnpm_hw *hw = &adapter->hw; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u32 priv_flags = 0; + // dbg("adapter priv is %x\n",iface->priv_flags); + + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_MAC_LOOPBACK) + priv_flags |= RNPM_MAC_LOOPBACK; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_SWITCH_LOOPBACK) + priv_flags |= RNPM_SWITCH_LOOPBACK; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_VEB_ENABLE) + priv_flags |= RNPM_VEB_ENABLE; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) + priv_flags |= RNPM_PCIE_CACHE_ALIGN_PATCH; + if (adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG) + priv_flags |= RNPM_PADDING_DEBUG; + if (adapter->priv_flags & RNPM_PRIV_FLAG_PTP_DEBUG) + priv_flags |= RNPM_PTP_FEATURE; + if (adapter->priv_flags & RNPM_PRIV_FLAG_SIMUATE_DOWN) + priv_flags |= RNPM_SIMULATE_DOWN; + if (adapter->priv_flags & RNPM_PRIV_FLAG_TO_RPU) + priv_flags |= RNPM_TO_RPU; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) + priv_flags |= RNPM_LEN_ERR; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) + priv_flags |= RNPM_FW_10G_1G_SFP_AUTO_DET_EN; + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY) + priv_flags |= RNPM_FORCE_SPEED_ABLITY; + if (adapter->priv_flags & RNPM_PRIV_FLAG_LLDP_EN_STAT) + priv_flags |= RNPM_LLDP_EN_STAT; + + return priv_flags; +} + +static int rnpm_priv_status_update(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + int i; + u32 priv = 0; + u32 data_old, data_new; + unsigned long flags; + + spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); + data_old = rd32(pf_adapter, RNPM_DMA_CONFIG); + data_new = data_old; + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + if (rnpm_port_is_valid(pf_adapter, i)) + priv |= pf_adapter->adapter[i]->priv_flags; + } + if (priv & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) { + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH; + SET_BIT(padding_enable, data_new); + } else { + pf_adapter->priv_flags &= + (~RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH); + CLR_BIT(padding_enable, data_new); + } + + if (priv & RNPM_PRIV_FLAG_MAC_LOOPBACK) { + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_MAC_LOOPBACK; + SET_BIT(mac_loopback, data_new); + } else { + pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_MAC_LOOPBACK); + CLR_BIT(mac_loopback, data_new); + } + + if (priv & RNPM_PRIV_FLAG_MAC_LOOPBACK) { + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_SWITCH_LOOPBACK; + SET_BIT(switch_loopback, data_new); + } else { + pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_SWITCH_LOOPBACK); + CLR_BIT(switch_loopback, data_new); + } + + if (priv & RNPM_PRIV_FLAG_VEB_ENABLE) { + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_VEB_ENABLE; + SET_BIT(veb_enable, data_new); + } else { + pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_VEB_ENABLE); + CLR_BIT(veb_enable, data_new); + } + + if (data_old != data_new) + wr32(pf_adapter, RNPM_DMA_CONFIG, data_new); + spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); + return 0; +} + +static int rnpm_priv_fw_10g_1g_auto_detch(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + unsigned long flags; + + spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); + + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) + rnpm_hw_set_fw_10g_1g_auto_detch(&adapter->hw, 1); + else + rnpm_hw_set_fw_10g_1g_auto_detch(&adapter->hw, 0); + + spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); + return 0; +} + +static int rnpm_priv_err_mask_set(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u32 data_old, data_new; + unsigned long flags; + + spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); + data_new = data_old = rd32(pf_adapter, RNPM_ETH_ERR_MASK_VECTOR); + + if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) { + // pf_adapter->priv_flags |= RNPM_PRIV_FLAG_LEN_ERR; + data_new |= (ETH_ERR_PKT_LEN_ERR | ETH_ERR_HDR_LEN_ERR); + } else { + // pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_LEN_ERR); + data_new &= ~(ETH_ERR_PKT_LEN_ERR | ETH_ERR_HDR_LEN_ERR); + } + + if (data_old != data_new) + wr32(pf_adapter, RNPM_ETH_ERR_MASK_VECTOR, data_new); + spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); + return 0; +} + +/** + * rnpm_set_priv_flags - set private flags + * @dev: network interface device structure + * @flags: bit flags to be set + **/ +static int rnpm_set_priv_flags(struct net_device *netdev, u32 priv_flags) +{ + struct rnpm_adapter *adapter = + (struct rnpm_adapter *)netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + u32 orig_flags, new_flags; + + orig_flags = rd32(hw, RNPM_DMA_CONFIG); + new_flags = orig_flags; + + if (priv_flags & RNPM_MAC_LOOPBACK) { + SET_BIT(mac_loopback, new_flags); + adapter->priv_flags |= RNPM_PRIV_FLAG_MAC_LOOPBACK; + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_MAC_LOOPBACK) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_MAC_LOOPBACK); + CLR_BIT(mac_loopback, new_flags); + } + + if (priv_flags & RNPM_LLDP_EN_STAT) { + if (rnpm_mbx_lldp_port_enable(hw, true) == 0) { + // dump_stack(); + adapter->priv_flags |= RNPM_PRIV_FLAG_LLDP_EN_STAT; + } else { + rnpm_err("%s: set lldp enable failed!\n", + adapter->netdev->name); + adapter->priv_flags &= (~RNPM_PRIV_FLAG_LLDP_EN_STAT); + } + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_LLDP_EN_STAT) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_LLDP_EN_STAT); + rnpm_mbx_lldp_port_enable(hw, false); + } + + if (priv_flags & RNPM_MPE_RELOAD) + rnpm_rpu_mpe_start(adapter->pf_adapter); + + if (priv_flags & RNPM_SWITCH_LOOPBACK) { + SET_BIT(switch_loopback, new_flags); + adapter->priv_flags |= RNPM_PRIV_FLAG_SWITCH_LOOPBACK; + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_SWITCH_LOOPBACK) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_SWITCH_LOOPBACK); + CLR_BIT(switch_loopback, new_flags); + } + + if (priv_flags & RNPM_VEB_ENABLE) { + SET_BIT(veb_enable, new_flags); + adapter->priv_flags |= RNPM_PRIV_FLAG_VEB_ENABLE; + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_VEB_ENABLE) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_VEB_ENABLE); + CLR_BIT(veb_enable, new_flags); + } + + if (priv_flags & RNPM_PCIE_CACHE_ALIGN_PATCH) { + SET_BIT(padding_enable, new_flags); + adapter->priv_flags |= RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH; + } else if (adapter->priv_flags & + RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH); + CLR_BIT(padding_enable, new_flags); + } + + if (priv_flags & RNPM_PADDING_DEBUG) + adapter->priv_flags |= RNPM_PRIV_FLAG_PADDING_DEBUG; + else if (adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG) + adapter->priv_flags &= (~RNPM_PRIV_FLAG_PADDING_DEBUG); + + if (priv_flags & RNPM_PTP_FEATURE) { + adapter->priv_flags |= RNPM_PRIV_FLAG_PTP_DEBUG; + adapter->flags2 |= ~RNPM_FLAG2_PTP_ENABLED; + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_PTP_DEBUG) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_PTP_DEBUG); + adapter->flags2 &= (~RNPM_FLAG2_PTP_ENABLED); + } + + if (priv_flags & RNPM_SIMULATE_DOWN) { + adapter->priv_flags |= RNPM_PRIV_FLAG_SIMUATE_DOWN; + /* set check link again */ + adapter->flags |= RNPM_FLAG_NEED_LINK_UPDATE; + } else if (adapter->priv_flags & RNPM_PRIV_FLAG_SIMUATE_DOWN) { + adapter->priv_flags &= (~RNPM_PRIV_FLAG_SIMUATE_DOWN); + /* set check link again */ + adapter->flags |= RNPM_FLAG_NEED_LINK_UPDATE; + } + + if (priv_flags & RNPM_TO_RPU) + adapter->priv_flags |= RNPM_PRIV_FLAG_TO_RPU; + else if (adapter->priv_flags & RNPM_PRIV_FLAG_TO_RPU) + adapter->priv_flags &= (~RNPM_PRIV_FLAG_TO_RPU); + + if (priv_flags & RNPM_FW_10G_1G_SFP_AUTO_DET_EN) { + if (rnpm_card_partially_supported_10g_1g_sfp( + adapter->pf_adapter)) { + adapter->pf_adapter->priv_flags |= + RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN; + rnpm_priv_fw_10g_1g_auto_detch(adapter); + } else { + return -EOPNOTSUPP; + } + } else if (adapter->pf_adapter->priv_flags & + RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { + adapter->pf_adapter->priv_flags &= + (~RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN); + rnpm_priv_fw_10g_1g_auto_detch(adapter); + } + + if (priv_flags & RNPM_FORCE_SPEED_ABLITY) { + if (adapter->hw.max_speed_1g == 1) { + adapter->pf_adapter->priv_flags &= + ~RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY; + adapter->pf_adapter->force_10g_1g_speed_ablity = false; + + rnpm_err( + "%s: max speed is 1G cannot set force_speed_ablity priv-flags !\n", + adapter->netdev->name); + } else { + adapter->pf_adapter->priv_flags |= + RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY; + adapter->pf_adapter->force_10g_1g_speed_ablity = true; + } + } else if (adapter->pf_adapter->priv_flags & + RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY) { + adapter->pf_adapter->priv_flags &= + (~RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY); + rnpm_mbx_force_speed(hw, 0); + set_bit(RNPM_PF_LINK_CHANGE, &adapter->pf_adapter->flags); + adapter->pf_adapter->force_10g_1g_speed_ablity = false; + } + + if (priv_flags & RNPM_LEN_ERR) { + adapter->pf_adapter->priv_flags |= RNPM_PRIV_FLAG_LEN_ERR; + rnpm_priv_err_mask_set(adapter); + } else if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) { + adapter->pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_LEN_ERR); + rnpm_priv_err_mask_set(adapter); + } + + if (orig_flags != new_flags) { + /* we not support this in multiports */ + // if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) + // return -EINVAL; + wr32(hw, RNPM_DMA_CONFIG, new_flags); + + rnpm_priv_status_update(adapter); + } + + /* if ft_padding changed */ + if (CHK_BIT(padding_enable, orig_flags) != + CHK_BIT(padding_enable, new_flags)) + rnpm_msg_post_status(adapter, PF_FT_PADDING_STATUS); + return 0; +} + +/* ethtool register test data */ + +/** + * rnpm_get_coalesce - get a netdev's coalesce settings + * @netdev: the netdev to check + * @ec: ethtool coalesce data structure + * @kec: kernel coalesce parameter + * @extack: kernel extack parameter + * + * Gets the coalesce settings for a particular netdev. Note that if user has + * modified per-queue settings, this only guarantees to represent queue 0. See + * __rnpm_get_coalesce for more details. + **/ +static int +rnpm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce __maybe_unused *kernel_coal, + struct netlink_ext_ack __maybe_unused *extack) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + coal->use_adaptive_tx_coalesce = adapter->adaptive_tx_coal; + coal->tx_coalesce_usecs = adapter->tx_usecs; + coal->tx_coalesce_usecs_irq = 0; + coal->tx_max_coalesced_frames = adapter->tx_frames; + coal->tx_max_coalesced_frames_irq = adapter->tx_work_limit; + + coal->use_adaptive_rx_coalesce = adapter->adaptive_rx_coal; + coal->rx_coalesce_usecs_irq = 0; + coal->rx_coalesce_usecs = adapter->rx_usecs; + coal->rx_max_coalesced_frames = adapter->rx_frames; + coal->rx_max_coalesced_frames_irq = adapter->napi_budge; + + /* this is not support */ + coal->pkt_rate_low = 0; + coal->pkt_rate_high = 0; + coal->rx_coalesce_usecs_low = 0; + coal->rx_max_coalesced_frames_low = 0; + coal->tx_coalesce_usecs_low = 0; + coal->tx_max_coalesced_frames_low = 0; + coal->rx_coalesce_usecs_high = 0; + coal->rx_max_coalesced_frames_high = 0; + coal->tx_coalesce_usecs_high = 0; + coal->tx_max_coalesced_frames_high = 0; + coal->rate_sample_interval = 0; + + return 0; +} + +/** + * rnpm_set_coalesce - set coalesce settings for every queue on the netdev + * @netdev: the netdev to change + * @ec: ethtool coalesce settings + * @kec: kernel coalesce parameter + * @extack: kernel extack parameter + * + * This will set each queue to the same coalesce settings. + **/ +static int +rnpm_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce __maybe_unused *kernel_coal, + struct netlink_ext_ack __maybe_unused *extack) +{ + int reset = 0; + struct rnpm_adapter *adapter = netdev_priv(netdev); + u32 value; + /* we don't support close tx and rx coalesce */ + if (!(ec->use_adaptive_tx_coalesce) || !(ec->use_adaptive_rx_coalesce)) + return -EINVAL; + + if ((ec->tx_max_coalesced_frames_irq < RNPM_MIN_TX_WORK) || + (ec->tx_max_coalesced_frames_irq > RNPM_MAX_TX_WORK)) + return -EINVAL; + value = ALIGN(ec->tx_max_coalesced_frames_irq, RNPM_WORK_ALIGN); + if (adapter->tx_work_limit != value) { + reset = 1; + adapter->tx_work_limit = value; + } + + if ((ec->tx_max_coalesced_frames < RNPM_MIN_TX_FRAME) || + (ec->tx_max_coalesced_frames > RNPM_MAX_TX_FRAME)) + return -EINVAL; + if (adapter->tx_frames != ec->tx_max_coalesced_frames) { + reset = 1; + adapter->tx_frames = ec->tx_max_coalesced_frames; + } + + if ((ec->tx_coalesce_usecs < RNPM_MIN_TX_USEC) || + (ec->tx_coalesce_usecs > RNPM_MAX_TX_USEC)) + return -EINVAL; + if (adapter->tx_usecs != ec->tx_coalesce_usecs) { + reset = 1; + adapter->tx_usecs = ec->tx_coalesce_usecs; + } + + if ((ec->rx_max_coalesced_frames_irq < RNPM_MIN_RX_WORK) || + (ec->rx_max_coalesced_frames_irq > RNPM_MAX_RX_WORK)) + return -EINVAL; + value = ALIGN(ec->rx_max_coalesced_frames_irq, RNPM_WORK_ALIGN); + if (adapter->napi_budge != ec->rx_max_coalesced_frames_irq) { + reset = 1; + adapter->napi_budge = ec->rx_max_coalesced_frames_irq; + } + + if ((ec->rx_max_coalesced_frames < RNPM_MIN_RX_FRAME) || + (ec->rx_max_coalesced_frames > RNPM_MAX_RX_FRAME)) + return -EINVAL; + if (adapter->rx_frames != ec->rx_max_coalesced_frames) { + reset = 1; + adapter->rx_frames = ec->rx_max_coalesced_frames; + } + + if ((ec->rx_coalesce_usecs < RNPM_MIN_RX_USEC) || + (ec->rx_coalesce_usecs > RNPM_MAX_RX_USEC)) + return -EINVAL; + + if (adapter->rx_usecs != ec->rx_coalesce_usecs) { + reset = 1; + adapter->rx_usecs = ec->rx_coalesce_usecs; + } + + /* other setup is not supported */ + if ((ec->pkt_rate_low) || (ec->pkt_rate_high) || + (ec->rx_coalesce_usecs_low) || (ec->rx_max_coalesced_frames_low) || + (ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) || + (ec->rx_coalesce_usecs_high) || + (ec->rx_max_coalesced_frames_high) || + (ec->tx_coalesce_usecs_high) || + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval) || + (ec->tx_coalesce_usecs_irq) || (ec->rx_coalesce_usecs_irq)) + return -EINVAL; + + if (reset) + return rnpm_setup_tc(netdev, netdev_get_num_tc(netdev)); + + return 0; +} + +/** + * rnpm_get_ethtool_stats - copy stat values into supplied buffer + * @netdev: the netdev to collect stats for + * @stats: ethtool stats command structure + * @data: ethtool supplied buffer + * + * Copy the stats values for this netdev into the buffer. Expects data to be + * pre-allocated to the size returned by i40e_get_stats_count.. Note that all + * statistics must be copied in a static order, and the count must not change + * for a given netdev. See i40e_get_stats_count for more details. + * + * If a statistic is not currently valid (such as a disabled queue), this + * function reports its value as zero. + **/ +static void rnpm_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + struct net_device_stats *net_stats = &netdev->stats; + struct rnpm_ring *ring; + int i, j; + char *p = NULL; + + rnpm_update_stats(adapter); + + for (i = 0; i < RNPM_GLOBAL_STATS_LEN; i++) { + p = (char *)net_stats + rnpm_gstrings_net_stats[i].stat_offset; + data[i] = (rnpm_gstrings_net_stats[i].sizeof_stat == + sizeof(u64)) ? + *(u64 *)p : + *(u32 *)p; + } + + for (j = 0; j < RNPM_HWSTRINGS_STATS_LEN; j++, i++) { + p = (char *)adapter + rnpm_hwstrings_stats[j].stat_offset; + data[i] = (rnpm_hwstrings_stats[j].sizeof_stat == sizeof(u64)) ? + *(u64 *)p : + *(u32 *)p; + } + + BUG_ON(RNPM_NUM_TX_QUEUES != RNPM_NUM_RX_QUEUES); + + for (j = 0; j < RNPM_NUM_TX_QUEUES; j++) { + /* tx-ring */ + ring = adapter->tx_ring[j]; + + if (!ring) { + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + + continue; + } + + data[i++] = ring->stats.packets; + data[i++] = ring->stats.bytes; + data[i++] = ring->tx_stats.restart_queue; + data[i++] = ring->tx_stats.tx_busy; + data[i++] = ring->tx_stats.tx_done_old; + data[i++] = ring->tx_stats.clean_desc; + data[i++] = ring->tx_stats.poll_count; + data[i++] = ring->tx_stats.irq_more_count; + /* rnpm_tx_queue_ring_stat */ + data[i++] = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD( + ring->rnpm_queue_idx)); + data[i++] = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL( + ring->rnpm_queue_idx)); + data[i++] = ring->next_to_clean; + data[i++] = ring->next_to_use; + data[i++] = ring->tx_stats.send_bytes; + data[i++] = ring->tx_stats.send_bytes_to_hw; + data[i++] = ring->tx_stats.todo_update; + data[i++] = ring->tx_stats.send_done_bytes; + data[i++] = ring->tx_stats.vlan_add; + if (ring->tx_stats.tx_next_to_clean == -1) + data[i++] = ring->count; + else + data[i++] = ring->tx_stats.tx_next_to_clean; + data[i++] = ring->tx_stats.tx_irq_miss; + data[i++] = ring->tx_stats.tx_equal_count; + + /* rx-ring */ + ring = adapter->rx_ring[j]; + + if (!ring) { + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + data[i++] = 0; + continue; + } + data[i++] = ring->stats.packets; + data[i++] = ring->stats.bytes; + data[i++] = ring->rx_stats.driver_drop_packets; + data[i++] = ring->rx_stats.rsc_count; + data[i++] = ring->rx_stats.rsc_flush; + data[i++] = ring->rx_stats.non_eop_descs; + data[i++] = ring->rx_stats.alloc_rx_page_failed; + data[i++] = ring->rx_stats.alloc_rx_buff_failed; + data[i++] = ring->rx_stats.csum_err; + data[i++] = ring->rx_stats.csum_good; + data[i++] = ring->rx_stats.poll_again_count; + data[i++] = ring->rx_stats.vlan_remove; + data[i++] = ring->rx_stats.alloc_rx_page; + /* rnpm_rx_queue_ring_stat */ + data[i++] = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD( + ring->rnpm_queue_idx)); + data[i++] = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_TAIL( + ring->rnpm_queue_idx)); + data[i++] = ring->next_to_use; + data[i++] = ring->next_to_clean; + if (ring->rx_stats.rx_next_to_clean == -1) + data[i++] = ring->count; + else + data[i++] = ring->rx_stats.rx_next_to_clean; + data[i++] = ring->rx_stats.rx_irq_miss; + data[i++] = ring->rx_stats.rx_equal_count; + data[i++] = ring->rx_stats.rx_poll_packets; + data[i++] = ring->rx_stats.rx_poll_avg_packets; + data[i++] = ring->rx_stats.rx_poll_itr; + } +} + +enum { + PART_FW, + PART_CFG, + PART_MACSN, + PART_PCSPHY, + PART_PXE, +}; + +#define UCFG_OFF 0x41000 +#define UCFG_SZ (4096) +#define PXE_OFF 0x4a000 +#define PXE_SZ (512 * 1024) + +static int rnpm_flash_firmware(struct rnpm_adapter *adapter, int region, + const u8 *data, int bytes) +{ + struct rnpm_hw *hw = &adapter->hw; + + switch (region) { + case PART_FW: { + if (*((u32 *)(data + 28)) != 0xA51BBEAF) + return -EINVAL; + if (bytes > PXE_OFF) { // fw with pxe + int err; + int wbytes_seg1 = bytes - PXE_OFF; + + if (wbytes_seg1 > PXE_SZ) + wbytes_seg1 = PXE_SZ; + + // fw + err = rnpm_fw_update(hw, PART_FW, data, UCFG_OFF); + if (err) + return err; + // skip ucfg flush only pxe + err = rnpm_fw_update(hw, PART_PXE, data + PXE_OFF, + wbytes_seg1); + if (err) + return err; + return 0; + } + break; + } + case PART_CFG: { + if (*((u32 *)(data)) != 0x00010cf9) + return -EINVAL; + break; + } + case PART_MACSN: { + break; + } + case PART_PCSPHY: { + if (*((u16 *)(data)) != 0x081d) + return -EINVAL; + break; + } + case PART_PXE: { + if ((*((u16 *)(data)) != 0xaa55) && + (*((u16 *)(data)) != 0x5a4d)) { + return -EINVAL; + } + break; + } + default: { + return -EINVAL; + } + } + + return rnpm_fw_update(hw, region, data, bytes); +} + +static int rnpm_flash_firmware_from_file(struct net_device *dev, + struct rnpm_adapter *adapter, + int region, const char *filename) +{ + const struct firmware *fw; + int rc; + + rc = request_firmware(&fw, filename, &dev->dev); + if (rc != 0) { + netdev_err(dev, "Error %d requesting firmware file: %s\n", rc, + filename); + return rc; + } + + rc = rnpm_flash_firmware(adapter, region, fw->data, fw->size); + + release_firmware(fw); + return rc; +} + +static int rnpm_flash_device(struct net_device *dev, + struct ethtool_flash *flash) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + + if (IS_VF(adapter->hw.pfvfnum)) { + netdev_err(dev, + "flashdev not supported from a virtual function\n"); + return -EINVAL; + } + + return rnpm_flash_firmware_from_file(dev, adapter, flash->region, + flash->data); +} + +static uint32_t rnpm_rss_indir_size(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + return rnpm_rss_indir_tbl_entries(adapter); +} + +static u32 rnpm_get_rxfh_key_size(struct net_device *netdev) +{ + return RNPM_RSS_KEY_SIZE; +} + +static void rnpm_get_reta(struct rnpm_adapter *adapter, u32 *indir) +{ + int i, reta_size = rnpm_rss_indir_tbl_entries(adapter); + u16 rss_m = adapter->ring_feature[RING_F_RSS].mask; + + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + rss_m = adapter->ring_feature[RING_F_RSS].indices - 1; + + for (i = 0; i < reta_size; i++) { + if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) + indir[i] = 0; + else + indir[i] = adapter->rss_indir_tbl[i] & rss_m; + } +} + +static int rnpm_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + if (indir) + rnpm_get_reta(adapter, indir); + + if (key) + memcpy(key, pf_adapter->rss_key, + rnpm_get_rxfh_key_size(netdev)); + + return 0; +} + +static int rnpm_rss_indir_tbl_max(struct rnpm_adapter *adapter) +{ + if (adapter->hw.rss_type == rnpm_rss_uv3p) + return 8; + else if (adapter->hw.rss_type == rnpm_rss_uv440) + return 128; + else if (adapter->hw.rss_type == rnpm_rss_n10) + return 128; + else + return 128; +} + +/** + * rnpm_set_rxfh - set the rx flow hash indirection table + * @netdev: network interface device structure + * @indir: indirection table + * @key: hash key + * @hfunc: hash function to use + * + * Returns -EINVAL if the table specifies an invalid queue id, otherwise + * returns 0 after programming the table. + **/ +static int rnpm_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u16 i; + u32 reta_entries = rnpm_rss_indir_tbl_entries(adapter); + unsigned long flags; + + if (hfunc) + return -EOPNOTSUPP; + + /* Verify user input. */ + if (indir) { + int max_queues = min_t(int, adapter->num_rx_queues, + rnpm_rss_indir_tbl_max(adapter)); + + /* in this mode ,do not change rss table */ + if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) + return -EINVAL; + /*Allow at least 2 queues w/ SR-IOV.*/ + if ((adapter->flags & RNPM_FLAG_SRIOV_ENABLED) && + (max_queues < 2)) + max_queues = 2; + + /* Verify user input. */ + for (i = 0; i < reta_entries; i++) + if (indir[i] >= max_queues) + return -EINVAL; + + /* store rss tbl */ + for (i = 0; i < reta_entries; i++) + adapter->rss_indir_tbl[i] = indir[i]; + + rnpm_store_reta(adapter); + } + + /* Fill out the rss hash key */ + if (key) { + /* not support key setup in multiports */ + if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) + return -EINVAL; + spin_lock_irqsave(&pf_adapter->key_setup_lock, flags); + memcpy(pf_adapter->rss_key, key, + rnpm_get_rxfh_key_size(netdev)); + rnpm_store_key(pf_adapter); + spin_unlock_irqrestore(&pf_adapter->key_setup_lock, flags); + } + + return 0; +} + +void rnpm_get_phy_statistics(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct phy_statistics ps; + + if (rnpm_mbx_get_phy_statistics(hw, (u8 *)&ps) != 0) + return; + + *data++ = ps.yt.pkg_ib_valid; + *data++ = ps.yt.pkg_ib_os_good; + *data++ = ps.yt.pkg_ib_us_good; + *data++ = ps.yt.pkg_ib_err; + *data++ = ps.yt.pkg_ib_os_bad; + *data++ = ps.yt.pkg_ib_frag; + *data++ = ps.yt.pkg_ib_nosfd; + *data++ = ps.yt.pkg_ob_valid; + *data++ = ps.yt.pkg_ob_os_good; + *data++ = ps.yt.pkg_ob_us_good; + *data++ = ps.yt.pkg_ob_err; + *data++ = ps.yt.pkg_ob_os_bad; + *data++ = ps.yt.pkg_ob_frag; + *data++ = ps.yt.pkg_ob_nosfd; +} + +static int rnpm_nway_reset(struct net_device *netdev) +{ + /* restart autonegotiation */ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + if (test_bit(__RNPM_DOWN, &adapter->state)) + return 0; + netdev_info(netdev, "NIC Link is Down\n"); + rnpm_down(adapter); + msleep(20); + rnpm_up(adapter); + return 0; +} +static const struct ethtool_ops rnpm_ethtool_ops = { + .supported_coalesce_params = 0 | ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES_IRQ | + ETHTOOL_COALESCE_MAX_FRAMES, + .get_link_ksettings = rnpm_get_link_ksettings, + .set_link_ksettings = rnpm_set_link_ksettings, + .get_drvinfo = rnpm_get_drvinfo, + .get_regs_len = rnpm_get_regs_len, + .get_regs = rnpm_get_regs, + .get_wol = rnpm_get_wol, + .set_wol = rnpm_set_wol, + .nway_reset = rnpm_nway_reset, + .get_link = ethtool_op_get_link, + .get_ringparam = rnpm_get_ringparam, + .set_ringparam = rnpm_set_ringparam, + .get_pauseparam = rnpm_get_pauseparam, + .set_pauseparam = rnpm_set_pauseparam, + .get_msglevel = rnpm_get_msglevel, + .set_msglevel = rnpm_set_msglevel, + .get_fecparam = rnpm_get_fecparam, + .set_fecparam = rnpm_set_fecparam, + .self_test = rnpm_diag_test, + .get_strings = rnpm_get_strings, + .set_phys_id = rnpm_set_phys_id, + .get_sset_count = rnpm_get_sset_count, + .get_priv_flags = rnpm_get_priv_flags, + .set_priv_flags = rnpm_set_priv_flags, + .get_ethtool_stats = rnpm_get_ethtool_stats, + .get_coalesce = rnpm_get_coalesce, + .set_coalesce = rnpm_set_coalesce, + .get_channels = rnpm_get_channels, + .set_channels = rnpm_set_channels, + .get_module_info = rnpm_get_module_info, + .get_module_eeprom = rnpm_get_module_eeprom, + .get_ts_info = rnpm_get_ts_info, + .get_rxfh_indir_size = rnpm_rss_indir_size, + .get_rxfh_key_size = rnpm_get_rxfh_key_size, + .get_rxfh = rnpm_get_rxfh, + .set_rxfh = rnpm_set_rxfh, + .get_dump_flag = rnpm_get_dump_flag, + .get_dump_data = rnpm_get_dump_data, + .set_dump = rnpm_set_dump, + .flash_device = rnpm_flash_device, + .get_ethtool_phy_stats = rnpm_get_phy_statistics, +}; + +void rnpm_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &rnpm_ethtool_ops; +} +// #endif /* SIOCETHTOOL */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_lib.c b/drivers/net/ethernet/mucse/rnpm/rnpm_lib.c new file mode 100644 index 000000000000..3a0e9eb9e09a --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_lib.c @@ -0,0 +1,1497 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ +#include "rnpm.h" +#include "rnpm_sriov.h" +#include "rnpm_common.h" + +#ifdef CONFIG_RNPM_DCB +/** + * rnpm_cache_ring_dcb_sriov - Descriptor ring to register mapping for SR-IOV + * @adapter: board private structure to initialize + * + * Cache the descriptor ring offsets for SR-IOV to the assigned rings. It + * will also try to cache the proper offsets if RSS/FCoE are enabled along + * with VMDq. + * + **/ +static bool rnpm_cache_ring_dcb_sriov(struct rnpm_adapter *adapter) +{ + struct rnpm_ring_feature *vmdq = &adapter->ring_feature[RING_F_VMDQ]; + int i; + u8 tcs = netdev_get_num_tc(adapter->netdev); + + /* verify we have DCB queueing enabled before proceeding */ + if (tcs <= 1) + return false; + + /* verify we have VMDq enabled before proceeding */ + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return false; + + return true; +} + +/* rnpm_get_first_reg_idx - Return first register index associated with ring */ +static void rnpm_get_first_reg_idx(struct rnpm_adapter *adapter, u8 tc, + unsigned int *tx, unsigned int *rx) +{ + struct net_device *dev = adapter->netdev; + struct rnpm_hw *hw = &adapter->hw; + u8 num_tcs = netdev_get_num_tc(dev); + + *tx = 0; + *rx = 0; + + switch (hw->mac.type) { + case rnpm_mac_82598EB: + /* TxQs/TC: 4 RxQs/TC: 8 */ + *tx = tc << 2; /* 0, 4, 8, 12, 16, 20, 24, 28 */ + *rx = tc << 3; /* 0, 8, 16, 24, 32, 40, 48, 56 */ + break; + case rnpm_mac_n10EB: + case rnpm_mac_X540: + if (num_tcs > 4) { + /* TCs : TC0/1 TC2/3 TC4-7 + * TxQs/TC: 32 16 8 + * RxQs/TC: 16 16 16 + */ + *rx = tc << 4; + if (tc < 3) + *tx = tc << 5; /* 0, 32, 64 */ + else if (tc < 5) + *tx = (tc + 2) << 4; /* 80, 96 */ + else + *tx = (tc + 8) << 3; /* 104, 112, 120 */ + } else { + /* TCs : TC0 TC1 TC2/3 + * TxQs/TC: 64 32 16 + * RxQs/TC: 32 32 32 + */ + *rx = tc << 5; + if (tc < 2) + *tx = tc << 6; /* 0, 64 */ + else + *tx = (tc + 4) << 4; /* 96, 112 */ + } + default: + break; + } +} + +/** + * rnpm_cache_ring_dcb - Descriptor ring to register mapping for DCB + * @adapter: board private structure to initialize + * + * Cache the descriptor ring offsets for DCB to the assigned rings. + * + **/ +static bool rnpm_cache_ring_dcb(struct rnpm_adapter *adapter) +{ + struct net_device *dev = adapter->netdev; + unsigned int tx_idx, rx_idx; + int tc, offset, rss_i, i; + u8 num_tcs = netdev_get_num_tc(dev); + + return true; +} + +#endif +/** + * rnpm_cache_ring_sriov - Descriptor ring to register mapping for sriov + * @adapter: board private structure to initialize + * + * SR-IOV doesn't use any descriptor rings but changes the default if + * no other mapping is used. + * + */ +static bool rnpm_cache_ring_sriov(struct rnpm_adapter *adapter) +{ + /* only proceed if VMDq is enabled */ + if (!(adapter->flags & RNPM_FLAG_VMDQ_ENABLED)) + return false; + + return true; +} + +/** + * rnpm_cache_ring_rss - Descriptor ring to register mapping for RSS + * @adapter: board private structure to initialize + * + * Cache the descriptor ring offsets for RSS to the assigned rings. + * + **/ +static bool rnpm_cache_ring_rss(struct rnpm_adapter *adapter) +{ + int i; + int ring_step = 1; + int ring_start = 0; + int ring_alloc = 1; + struct rnpm_ring *ring; + struct rnpm_hw *hw = &adapter->hw; + + switch (hw->mode) { + case MODE_NIC_MODE_1PORT: + ring_step = 1; + ring_start = adapter->port; + ring_alloc = 1; + break; + case MODE_NIC_MODE_1PORT_40G: + ring_step = 1; + ring_start = adapter->port; + ring_alloc = 1; + break; + case MODE_NIC_MODE_2PORT: + ring_step = 4; + ring_start = adapter->port * 2; + ring_alloc = 2; + break; + case MODE_NIC_MODE_4PORT: + ring_step = 4; + ring_start = adapter->port; + ring_alloc = 1; + break; + } + + for (i = 0; i < adapter->num_tx_queues; i++) { + ring = adapter->tx_ring[i]; + /* reset ring vars */ + ring->rnpm_queue_idx = + ring_start + (i / ring_alloc) * ring_step + + ((ring_alloc == 1) ? 0 : ((i % 2) ? 1 : 0)); + ring->dma_hw_addr = hw->hw_addr; + ring->dma_int_stat = + hw->hw_addr + RNPM_DMA_INT_STAT(ring->rnpm_queue_idx); + ring->dma_int_mask = ring->dma_int_stat + 4; + ring->dma_int_clr = ring->dma_int_stat + 8; + } + + for (i = 0; i < adapter->num_rx_queues; i++) { + ring = adapter->rx_ring[i]; + /* reset ring vars */ + ring->rnpm_queue_idx = + ring_start + (i / ring_alloc) * ring_step + + ((ring_alloc == 1) ? 0 : ((i % 2) ? 1 : 0)); + ring->dma_hw_addr = hw->hw_addr; + ring->dma_int_stat = + hw->hw_addr + RNPM_DMA_INT_STAT(ring->rnpm_queue_idx); + ring->dma_int_mask = ring->dma_int_stat + 4; + ring->dma_int_clr = ring->dma_int_stat + 8; + } + + return true; +} + +/** + * rnpm_cache_ring_register - Descriptor ring to register mapping + * @adapter: board private structure to initialize + * + * Once we know the feature-set enabled for the device, we'll cache + * the register offset the descriptor ring is assigned to. + * + * Note, the order the various feature calls is important. It must start with + * the "most" features enabled at the same time, then trickle down to the + * least amount of features turned on at once. + **/ +static void rnpm_cache_ring_register(struct rnpm_adapter *adapter) +{ + /* start with default case */ + +#ifdef CONFIG_RNPM_DCB + if (rnpm_cache_ring_dcb_sriov(adapter)) + return; + + if (rnpm_cache_ring_dcb(adapter)) + return; + +#endif + /* sriov ring alloc is added before, this maybe no use */ + if (rnpm_cache_ring_sriov(adapter)) + return; + + rnpm_cache_ring_rss(adapter); +} + +#define RNPM_RSS_64Q_MASK 0x3F +#define RNPM_RSS_16Q_MASK 0xF +#define RNPM_RSS_8Q_MASK 0x7 +#define RNPM_RSS_4Q_MASK 0x3 +#define RNPM_RSS_2Q_MASK 0x1 +#define RNPM_RSS_DISABLED_MASK 0x0 + +#ifdef CONFIG_RNPM_DCB +/** + * rnpm_set_dcb_sriov_queues: Allocate queues for SR-IOV devices w/ DCB + * @adapter: board private structure to initialize + * + * When SR-IOV (Single Root IO Virtualiztion) is enabled, allocate queues + * and VM pools where appropriate. Also assign queues based on DCB + * priorities and map accordingly.. + * + **/ +static bool rnpm_set_dcb_sriov_queues(struct rnpm_adapter *adapter) +{ + int i; + u16 vmdq_i = adapter->ring_feature[RING_F_VMDQ].limit; + u16 vmdq_m = 0; + u8 tcs = netdev_get_num_tc(adapter->netdev); + + /* verify we have DCB queueing enabled before proceeding */ + if (tcs <= 1) + return false; + + /* verify we have VMDq enabled before proceeding */ + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return false; + + /* Add starting offset to total pool count */ + vmdq_i += adapter->ring_feature[RING_F_VMDQ].offset; + + /* 16 pools w/ 8 TC per pool */ + if (tcs > 4) { + vmdq_i = min_t(u16, vmdq_i, 16); + vmdq_m = RNPM_n10_VMDQ_8Q_MASK; + /* 32 pools w/ 4 TC per pool */ + } else { + vmdq_i = min_t(u16, vmdq_i, 32); + vmdq_m = RNPM_n10_VMDQ_4Q_MASK; + } + + /* remove the starting offset from the pool count */ + vmdq_i -= adapter->ring_feature[RING_F_VMDQ].offset; + + /* save features for later use */ + adapter->ring_feature[RING_F_VMDQ].indices = vmdq_i; + adapter->ring_feature[RING_F_VMDQ].mask = vmdq_m; + + /* We do not support DCB, VMDq, and RSS all simultaneously + * so we will disable RSS since it is the lowest priority + */ + adapter->ring_feature[RING_F_RSS].indices = 2; + adapter->ring_feature[RING_F_RSS].mask = RNPM_RSS_DISABLED_MASK; + + /* disable ATR as it is not supported when VMDq is enabled */ + adapter->flags &= ~RNPM_FLAG_FDIR_HASH_CAPABLE; + + adapter->num_tx_queues = vmdq_i * tcs; + adapter->num_rx_queues = vmdq_i * tcs; + + /* configure TC to queue mapping */ + for (i = 0; i < tcs; i++) + netdev_set_tc_queue(adapter->netdev, i, 1, i); + + return true; +} + +static bool rnpm_set_dcb_queues(struct rnpm_adapter *adapter) +{ + struct net_device *dev = adapter->netdev; + struct rnpm_ring_feature *f; + int rss_i, rss_m, i; + int tcs; + + /* Map queue offset and counts onto allocated tx queues */ + tcs = netdev_get_num_tc(dev); + + /* verify we have DCB queueing enabled before proceeding */ + if (tcs <= 1) + return false; + return true; +} + +#endif +/** + * rnpm_set_sriov_queues - Allocate queues for SR-IOV devices + * @adapter: board private structure to initialize + * + * When SR-IOV (Single Root IO Virtualiztion) is enabled, allocate queues + * and VM pools where appropriate. If RSS is available, then also try and + * enable RSS and map accordingly. + * + **/ +static bool rnpm_set_sriov_queues(struct rnpm_adapter *adapter) +{ + // u16 vmdq_i = adapter->ring_feature[RING_F_VMDQ].limit; + u16 vmdq_m = 0; + u16 rss_i = adapter->ring_feature[RING_F_RSS].limit; + u16 rss_m = RNPM_RSS_DISABLED_MASK; + + /* only proceed if SR-IOV is enabled */ + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return false; + + /* save features for later use */ + adapter->ring_feature[RING_F_VMDQ].indices = + adapter->max_ring_pair_counts - 1; + adapter->ring_feature[RING_F_VMDQ].mask = vmdq_m; + + /* limit RSS based on user input and save for later use */ + adapter->ring_feature[RING_F_RSS].indices = rss_i; + adapter->ring_feature[RING_F_RSS].mask = rss_m; + + adapter->num_rx_queues = PF_RING_CNT_WHEN_IOV_ENABLED; + adapter->num_tx_queues = PF_RING_CNT_WHEN_IOV_ENABLED; + + /* disable ATR as it is not supported when VMDq is enabled */ + adapter->flags &= ~RNPM_FLAG_FDIR_HASH_CAPABLE; + + return true; +} + +u32 rnpm_rss_indir_tbl_entries(struct rnpm_adapter *adapter) +{ + if (adapter->hw.rss_type == rnpm_rss_uv3p) + return 8; + else if (adapter->hw.rss_type == rnpm_rss_uv440) + return 128; + else if (adapter->hw.rss_type == rnpm_rss_n10) + return 128; + else + return 128; +} +/** + * rnpm_set_rss_queues - Allocate queues for RSS + * @adapter: board private structure to initialize + * + * This is our "base" multiqueue mode. RSS (Receive Side Scaling) will try + * to allocate one Rx queue per CPU, and if available, one Tx queue per CPU. + * + **/ +static bool rnpm_set_rss_queues(struct rnpm_adapter *adapter) +{ + struct rnpm_ring_feature *f; + u16 rss_i; + + f = &adapter->ring_feature[RING_F_RSS]; + rss_i = f->limit; + /* set limit -> indices */ + f->indices = rss_i; + /* should init rss mask */ + if (adapter->hw.rss_type == rnpm_rss_uv3p) { + f->mask = RNPM_RSS_8Q_MASK; + } else if (adapter->hw.rss_type == rnpm_rss_uv440) { + f->mask = RNPM_RSS_64Q_MASK; + /* maybe not good */ + } else if (adapter->hw.rss_type == rnpm_rss_n10) { + f->mask = RNPM_RSS_64Q_MASK; + /* maybe not good */ + } + + /* set rss_i -> adapter->num_tx_queues */ + adapter->num_tx_queues = + min_t(int, rss_i, adapter->max_ring_pair_counts); + adapter->num_rx_queues = adapter->num_tx_queues; + + rnpm_dbg("[%s] limit:%d indices:%d queues:%d\n", adapter->netdev->name, + f->limit, f->indices, adapter->num_tx_queues); + + return true; +} + +static void rnpm_set_num_queues(struct rnpm_adapter *adapter) +{ + /* Start with base case */ + adapter->num_tx_queues = 1; + adapter->num_rx_queues = 1; + +#ifdef CONFIG_RNPM_DCB + if (rnpm_set_dcb_sriov_queues(adapter)) + return; + + if (rnpm_set_dcb_queues(adapter)) + return; + +#endif + if (rnpm_set_sriov_queues(adapter)) + return; + /* at last we support rss */ + rnpm_set_rss_queues(adapter); +} + +int rnpm_acquire_msix_vectors(struct rnpm_adapter *adapter, int vectors) +{ + int err; + int vectors_per_port = 0; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + + dbg("%s %d\n", __func__, vectors); + + switch (hw->mode) { + case MODE_NIC_MODE_1PORT: + vectors_per_port = vectors - adapter->num_other_vectors; + break; + case MODE_NIC_MODE_1PORT_40G: + vectors_per_port = vectors - adapter->num_other_vectors; + break; + case MODE_NIC_MODE_2PORT: + vectors_per_port = (vectors - adapter->num_other_vectors) / 2; + break; + case MODE_NIC_MODE_4PORT: + vectors_per_port = (vectors - adapter->num_other_vectors) / 4; + break; + } + /* if msix is init before, return here */ + adapter->num_q_vectors = min(vectors_per_port, adapter->max_q_vectors); + if (pf_adapter->msix_entries) + return 0; + + err = pci_enable_msix_range(adapter->pdev, adapter->msix_entries, + vectors, vectors); + if (err < 0) { + rnpm_err("pci_enable_msix failed: req:%d err:%d\n", vectors, + err); + kfree(adapter->msix_entries); + adapter->msix_entries = NULL; + return -EINVAL; + } + /* Adjust for only the vectors we'll use, which is minimum + * of max_msix_q_vectors + NON_Q_VECTORS, or the number of + * vectors we were allocated. + */ + + return 0; +} + +static void rnpm_add_ring(struct rnpm_ring *ring, + struct rnpm_ring_container *head) +{ + ring->next = head->ring; + head->ring = ring; + head->count++; +} + +static inline void rnpm_irq_disable_queues(struct rnpm_q_vector *q_vector) +{ + struct rnpm_ring *ring; + + rnpm_for_each_ring(ring, q_vector->tx) { + // update usecs + rnpm_wr_reg(ring->dma_int_mask, (RX_INT_MASK | TX_INT_MASK)); + } +} + +static enum hrtimer_restart irq_miss_check(struct hrtimer *hrtimer) +{ + struct rnpm_q_vector *q_vector; + struct rnpm_ring *ring; + struct rnpm_tx_desc *eop_desc; + struct rnpm_adapter *adapter; + + int tx_next_to_clean; + int tx_next_to_use; + + struct rnpm_tx_buffer *tx_buffer; + union rnpm_rx_desc *rx_desc; + + q_vector = container_of(hrtimer, struct rnpm_q_vector, + irq_miss_check_timer); + adapter = q_vector->adapter; + + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + goto do_self_napi; + set_bit(RNPM_IRQ_MISS_HANDLE_DONE, &q_vector->flags); + // check tx irq miss + rnpm_for_each_ring(ring, q_vector->tx) { + tx_next_to_clean = ring->next_to_clean; + tx_next_to_use = ring->next_to_use; + // have work to do + if (tx_next_to_use != tx_next_to_clean) { + tx_buffer = &ring->tx_buffer_info[tx_next_to_clean]; + eop_desc = tx_buffer->next_to_watch; + // have tx done + // next_to_watch maybe null in some condition + if (eop_desc) { + if ((eop_desc->vlan_cmd & + cpu_to_le32(RNPM_TXD_STAT_DD))) { + // close irq + // printk("call irq self\n"); + rnpm_irq_disable_queues(q_vector); + napi_schedule_irqoff(&q_vector->napi); + goto do_self_napi; + } + } + } + } + + // check rx irq + rnpm_for_each_ring(ring, q_vector->rx) { + rx_desc = RNPM_RX_DESC(ring, ring->next_to_clean); + if (rx_desc == NULL) { + /* if one desc is null, mybe the verctor is freed, exit directly */ + goto do_self_napi; + } + + if (rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_DD)) { + // should check rx not zero + int size; + + size = le16_to_cpu(rx_desc->wb.len); + if (size) { + rnpm_irq_disable_queues(q_vector); + napi_schedule_irqoff(&q_vector->napi); + } else { + // try to reset pf + struct rnpm_pf_adapter *pf_adapter = + adapter->pf_adapter; + set_bit(RNPM_PF_RESET, &pf_adapter->flags); + } + goto do_self_napi; + } + } + +do_self_napi: + clear_bit(RNPM_IRQ_MISS_HANDLE_DONE, &q_vector->flags); + return HRTIMER_NORESTART; +} + +/** + * rnpm_alloc_q_vector - Allocate memory for a single interrupt vector + * @adapter: board private structure to initialize + * @v_count: q_vectors allocated on adapter, used for ring interleaving + * @v_idx: index of vector in adapter struct + * @txr_count: total number of Tx rings to allocate + * @txr_idx: index of first Tx ring to allocate + * @rxr_count: total number of Rx rings to allocate + * @rxr_idx: index of first Rx ring to allocate + * + * We allocate one q_vector. If allocation fails we return -ENOMEM. + **/ +static int rnpm_alloc_q_vector(struct rnpm_adapter *adapter, int eth_queue_idx, + int v_idx, int r_idx, int r_count, int step) +{ + struct rnpm_q_vector *q_vector; + struct rnpm_ring *ring; + struct rnpm_hw *hw = &adapter->hw; + int node = NUMA_NO_NODE; + int cpu = -1; + int ring_count, size; + int txr_count, rxr_count, idx; + int rxr_idx = r_idx, txr_idx = r_idx; + + DPRINTK(PROBE, INFO, + "eth_queue_idx:%d v_idx:%d(off:%d) ring:%d ring_cnt:%d step:%d\n", + eth_queue_idx, v_idx, adapter->vector_off, r_idx, r_count, + step); + + txr_count = rxr_count = r_count; + + ring_count = txr_count + rxr_count; + + /* alloc ring memory together with q_vector */ + size = sizeof(struct rnpm_q_vector) + + (sizeof(struct rnpm_ring) * ring_count); + + /* should minis adapter->vector_off */ + if (cpu_online(v_idx - adapter->vector_off)) { + /* test feiteng, assign Manually */ + /* cpu 48 - 55 */ + /* node 6 */ + + /* cpu 1 - 7 */ + //cpu = 1 + v_idx - adapter->vector_off; + cpu = v_idx - adapter->vector_off; + node = cpu_to_node(cpu); + } + + /* allocate q_vector and rings */ + q_vector = kzalloc_node(size, GFP_KERNEL, node); + if (!q_vector) + q_vector = kzalloc(size, GFP_KERNEL); + if (!q_vector) + return -ENOMEM; + + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); + /* setup affinity mask and node */ + q_vector->numa_node = node; + /* initialize timer */ + q_vector->irq_check_usecs = RNPM_IRQ_CHECK_USEC; + //q_vector->new_rx_count = RNPM_PKT_TIMEOUT; + //q_vector->old_rx_count = RNPM_PKT_TIMEOUT; + + hrtimer_init(&q_vector->irq_miss_check_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_PINNED); + q_vector->irq_miss_check_timer.function = irq_miss_check; + + /* initialize NAPI */ + netif_napi_add_weight(adapter->netdev, &q_vector->napi, rnpm_poll, + adapter->napi_budge); + /* tie q_vector and adapter together */ + adapter->q_vector[v_idx - adapter->vector_off] = q_vector; + q_vector->adapter = adapter; + /* this indicate vector table */ + q_vector->v_idx = v_idx; + + /* initialize work limits */ + q_vector->tx.work_limit = adapter->tx_work_limit; + + q_vector->rx.itr = q_vector->itr = adapter->rx_frames; +#ifdef CONFIG_HZ + q_vector->factor = DIV_ROUND_UP(1000, CONFIG_HZ); +#else + q_vector->factor = 1; +#endif + + /* initialize pointer to rings */ + ring = q_vector->ring; + + for (idx = 0; idx < txr_count; idx++) { + /* assign generic ring traits */ + ring->dev = &adapter->pdev->dev; + ring->netdev = adapter->netdev; + + /* configure backlink on ring */ + ring->q_vector = q_vector; + + /* update q_vector Tx values */ + rnpm_add_ring(ring, &q_vector->tx); + + /* apply Tx specific ring traits */ + ring->count = adapter->tx_ring_item_count; + ring->queue_index = eth_queue_idx + idx; + + /* rnpm_queue_idx can be changed after */ + /* it is used to location hw reg */ + ring->rnpm_queue_idx = txr_idx; + ring->dma_int_stat = + hw->hw_addr + RNPM_DMA_INT_STAT(ring->rnpm_queue_idx); + ring->dma_int_mask = ring->dma_int_stat + 4; + ring->dma_int_clr = ring->dma_int_stat + 8; + ring->device_id = adapter->pdev->device; + ring->pfvfnum = hw->pfvfnum; + + /* assign ring to adapter */ + adapter->tx_ring[ring->queue_index] = ring; + + /* update count and index */ + txr_idx += step; + + rnpm_dbg("\t\t%s:vector[%d] <--RNPM TxRing:%d, eth_queue:%d\n", + adapter->netdev->name, v_idx, ring->rnpm_queue_idx, + ring->queue_index); + + /* push pointer to next ring */ + ring++; + } + + for (idx = 0; idx < rxr_count; idx++) { + /* assign generic ring traits */ + ring->dev = &adapter->pdev->dev; + ring->netdev = adapter->netdev; + + /* configure backlink on ring */ + ring->q_vector = q_vector; + + /* update q_vector Rx values */ + rnpm_add_ring(ring, &q_vector->rx); + + /* apply Rx specific ring traits */ + ring->count = adapter->rx_ring_item_count; + /* rnpm_queue_idx can be changed after */ + /* it is used to location hw reg */ + ring->queue_index = eth_queue_idx + idx; + ring->rnpm_queue_idx = rxr_idx; + ring->dma_int_stat = + hw->hw_addr + RNPM_DMA_INT_STAT(ring->rnpm_queue_idx); + ring->dma_int_mask = ring->dma_int_stat + 4; + ring->dma_int_clr = ring->dma_int_stat + 8; + ring->device_id = adapter->pdev->device; + ring->pfvfnum = hw->pfvfnum; + + /* assign ring to adapter */ + adapter->rx_ring[ring->queue_index] = ring; + rnpm_dbg("\t\t%s:vector[%d] <--RNPM RxRing:%d, eth_queue:%d\n", + adapter->netdev->name, v_idx, ring->rnpm_queue_idx, + ring->queue_index); + + /* update count and index */ + rxr_idx += step; + + /* push pointer to next ring */ + ring++; + } + + return 0; +} + +/** + * rnpm_free_q_vector - Free memory allocated for specific interrupt vector + * @adapter: board private structure to initialize + * @v_idx: Index of vector to be freed + * + * This function frees the memory allocated to the q_vector. In addition if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void rnpm_free_q_vector(struct rnpm_adapter *adapter, int v_idx) +{ + struct rnpm_q_vector *q_vector = adapter->q_vector[v_idx]; + struct rnpm_ring *ring; + + dbg("v_idx:%d\n", v_idx); + + hrtimer_cancel(&q_vector->irq_miss_check_timer); + + rnpm_for_each_ring(ring, q_vector->tx) + adapter->tx_ring[ring->queue_index] = NULL; + + rnpm_for_each_ring(ring, q_vector->rx) + adapter->rx_ring[ring->queue_index] = NULL; + + adapter->q_vector[v_idx] = NULL; + netif_napi_del(&q_vector->napi); + + /* rnpm_get_stats64() might access the rings on this vector, + * we must wait a grace period before freeing it. + */ + kfree_rcu(q_vector, rcu); +} + +/** + * rnpm_alloc_q_vectors - Allocate memory for interrupt vectors + * @adapter: board private structure to initialize + * + * We allocate one q_vector per queue interrupt. If allocation fails we + * return -ENOMEM. + **/ +static int rnpm_alloc_q_vectors(struct rnpm_adapter *adapter) +{ + int v_idx = adapter->vector_off; + struct rnpm_hw *hw = &adapter->hw; + int ring_idx = 0; + int r_remaing = + min_t(int, adapter->num_tx_queues, adapter->num_rx_queues); + int ring_step = 1; + int err, ring_cnt, v_remaing = adapter->num_q_vectors; + int q_vector_nums = 0; + + if ((adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) { + ring_idx = 0; + // use 1ring when vf enabled + /* only 2 rings when sriov enabled */ + /* from back */ + ring_idx = adapter->max_ring_pair_counts - + ring_step * PF_RING_CNT_WHEN_IOV_ENABLED; + r_remaing = PF_RING_CNT_WHEN_IOV_ENABLED; + } + BUG_ON(adapter->num_q_vectors == 0); + /* start from port num */ + ring_idx = adapter->port; + /* eth_queue_idx always start from 0 */ + adapter->eth_queue_idx = 0; + switch (hw->mode) { + case MODE_NIC_MODE_1PORT: + ring_step = 1; + break; + case MODE_NIC_MODE_1PORT_40G: + ring_step = 1; + break; + case MODE_NIC_MODE_2PORT: + ring_step = 2; + break; + case MODE_NIC_MODE_4PORT: + ring_step = 4; + break; + } + + rnpm_dbg("r_remaing:%d, ring_step:%d num_q_vectors:%d\n", r_remaing, + ring_step, v_remaing); + + /* can support muti rings in one q_vector */ + for (; r_remaing > 0 && v_remaing > 0; v_remaing--) { + ring_cnt = DIV_ROUND_UP(r_remaing, v_remaing); + err = rnpm_alloc_q_vector(adapter, adapter->eth_queue_idx, + v_idx, ring_idx, ring_cnt, ring_step); + if (err) + goto err_out; + ring_idx += ring_step * ring_cnt; + r_remaing -= ring_cnt; + v_idx++; + q_vector_nums++; + adapter->eth_queue_idx += ring_cnt; + } + /* should fix the real used q_vectors_nums */ + adapter->num_q_vectors = q_vector_nums; + return 0; + +err_out: + adapter->num_tx_queues = 0; + adapter->num_rx_queues = 0; + adapter->num_q_vectors = 0; + + while (v_idx--) + rnpm_free_q_vector(adapter, v_idx); + + return -ENOMEM; +} + +/** + * rnpm_free_q_vectors - Free memory allocated for interrupt vectors + * @adapter: board private structure to initialize + * + * This function frees the memory allocated to the q_vectors. In addition if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void rnpm_free_q_vectors(struct rnpm_adapter *adapter) +{ + int v_idx = adapter->num_q_vectors; + + adapter->num_rx_queues = 0; + adapter->num_tx_queues = 0; + adapter->num_q_vectors = 0; + + while (v_idx--) + rnpm_free_q_vector(adapter, v_idx); +} + +static void rnpm_reset_interrupt_capability(struct rnpm_adapter *adapter) +{ + kfree(adapter->msix_entries); + adapter->msix_entries = NULL; +} + +/** + * rnpm_set_interrupt_capability - set MSI-X or MSI if supported + * @adapter: board private structure to initialize + * + * Attempt to configure the interrupts using the best available + * capabilities of the hardware and the kernel. + **/ +static int rnpm_set_interrupt_capability(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + int v_budget, err = 0; + int msix_offset; + + v_budget = adapter->max_ring_pair_counts; + /* in one ring mode should reset v_budget */ + v_budget = min_t(int, v_budget, num_online_cpus()); + v_budget = min_t(int, v_budget, adapter->max_msix_counts); + + /* muti port use only one other vector */ + /* add one other vectors */ + adapter->msix_entries = + kcalloc(v_budget, sizeof(struct msix_entry), GFP_KERNEL); + if (!adapter->msix_entries) { + rnpm_err("alloc msix_entries failed!\n"); + return -EINVAL; + } + dbg("[%s] adapter:%p msix_entry:%p vector:%d\n", __func__, adapter, + adapter->msix_entries, adapter->vector_off); + + msix_offset = adapter->vector_off; + memcpy((u8 *)adapter->msix_entries, + (u8 *)pf_adapter->msix_entries + + sizeof(struct msix_entry) * msix_offset, + sizeof(struct msix_entry) * v_budget); + + adapter->num_q_vectors = min(v_budget, adapter->max_q_vectors); + + rnpm_dbg( + "adapter%d alloc vectors: cnt:%d [%d~%d] num_q_vectors:%d msix_offset %d\n", + adapter->bd_number, v_budget, adapter->vector_off, + adapter->vector_off + v_budget - 1, adapter->num_q_vectors, + msix_offset); + + return err; +} + +/** + * rnpm_init_interrupt_scheme - Determine proper interrupt scheme + * @adapter: board private structure to initialize + * + * We determine which interrupt scheme to use based on... + * - Hardware queue count (num_*_queues) + * - defined by miscellaneous hardware support/features (RSS, etc.) + **/ +int rnpm_init_interrupt_scheme(struct rnpm_adapter *adapter) +{ + int err; + // struct net_device *netdev = adapter->netdev; + + /* Number of supported queues */ + rnpm_set_num_queues(adapter); + + /* Set interrupt mode */ + rnpm_set_interrupt_capability(adapter); + + err = rnpm_alloc_q_vectors(adapter); + if (err) { + e_dev_err("Unable to allocate memory for queue vectors\n"); + goto err_alloc_q_vectors; + } + rnpm_cache_ring_register(adapter); + + DPRINTK(PROBE, INFO, + "Multiqueue %s: Rx Queue count = %u, Tx Queue count = %u\n\n", + (adapter->num_rx_queues > 1) ? "Enabled" : "Disabled", + adapter->num_rx_queues, adapter->num_tx_queues); + + set_bit(__RNPM_DOWN, &adapter->state); + return 0; + +err_alloc_q_vectors: + rnpm_reset_interrupt_capability(adapter); + return err; +} + +/** + * rnpm_clear_interrupt_scheme - Clear the current interrupt scheme settings + * @adapter: board private structure to clear interrupt scheme on + * + * We go through and clear interrupt specific resources and reset the structure + * to pre-load conditions + **/ +void rnpm_clear_interrupt_scheme(struct rnpm_adapter *adapter) +{ + adapter->num_tx_queues = 0; + adapter->num_rx_queues = 0; + + rnpm_free_q_vectors(adapter); + rnpm_reset_interrupt_capability(adapter); +} + +/** + * rnpm_tx_ctxtdesc - Send a control desc to hw + * @tx_ring: target ring of this control desc + * @mss_seg_len: mss length + * @l4_hdr_len: l4 length + * @tunnel_hdr_len: tunnel_hdr_len + * @inner_vlan_tag: inner_vlan_tag + * @type_tucmd: cmd + * + **/ + +void rnpm_tx_ctxtdesc(struct rnpm_ring *tx_ring, u32 mss_len_vf_num, + u32 inner_vlan_tunnel_len, u32 type_tucmd) +{ + struct rnpm_tx_ctx_desc *context_desc; + u16 i = tx_ring->next_to_use; + // struct rnpm_adapter *adapter = RING2ADAPT(tx_ring); + + context_desc = RNPM_TX_CTXTDESC(tx_ring, i); + + i++; + tx_ring->next_to_use = (i < tx_ring->count) ? i : 0; + + /* set bits to identify this as an advanced context descriptor */ + //type_tucmd |= RNPM_TXD_CMD_RS | RNPM_TXD_CTX_CTRL_DESC; + type_tucmd |= RNPM_TXD_CTX_CTRL_DESC; + + if (inner_vlan_tunnel_len & 0x00ffff00) { + /* if a inner vlan */ + type_tucmd |= RNPM_TXD_CMD_INNER_VLAN; + } + + context_desc->mss_len_vf_num = cpu_to_le32(mss_len_vf_num); + context_desc->inner_vlan_tunnel_len = + cpu_to_le32(inner_vlan_tunnel_len); + context_desc->resv_cmd = cpu_to_le32(type_tucmd); +#ifdef RNPM_IOV_VEB_BUG_NOT_FIXED + if (tx_ring->q_vector->adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + context_desc->inner_vlan_tunnel_len |= VF_VEB_MARK; + //((u8*)&context_desc->mss_len_vf_num)[2] = + // tx_ring->q_vector->adapter->veb_vfnum; + } +#endif + buf_dump_line("ctx ", __LINE__, context_desc, sizeof(*context_desc)); +} + +void rnpm_maybe_tx_ctxtdesc(struct rnpm_ring *tx_ring, + struct rnpm_tx_buffer *first, u32 type_tucmd) +{ + struct rnpm_adapter *adapter = netdev_priv((tx_ring)->netdev); + /* sriov mode pf use the last vf */ + if (first->ctx_flag) { + if (adapter->priv_flags & RNPM_PRIV_FLAG_TX_PADDING) { + if (!first->gso_need_padding) + type_tucmd |= RNPM_TXD_MTI_CRC_PAD_CTRL; + first->gso_need_padding = false; + } + + rnpm_tx_ctxtdesc(tx_ring, first->mss_len_vf_num, + first->inner_vlan_tunnel_len, type_tucmd); + } +} + +void rnpm_store_reta(struct rnpm_adapter *adapter) +{ + u32 i, reta_entries = rnpm_rss_indir_tbl_entries(adapter); + struct rnpm_hw *hw = &adapter->hw; + u32 reta = 0; + // u8 *indir_tbl = adapter->rss_indir_tbl; + /* relative with rss table */ + u32 port = adapter->port; + struct rnpm_ring *rx_ring; + + /* Write redirection table to HW */ + for (i = 0; i < reta_entries; i++) { + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + reta = adapter->rss_indir_tbl[i]; + } else { + rx_ring = adapter->rx_ring[adapter->rss_indir_tbl[i]]; + if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) { + /* clean table to zero */ + reta = adapter->port; + } else { + int port_offset = + rd32(hw, RNPM_ETH_TC_PORT_OFFSET_TABLE( + adapter->port)); + + reta = rx_ring->rnpm_queue_idx - port_offset; + } + } + if (hw->rss_type == rnpm_rss_uv3p) + wr32(hw, RNPM_ETH_RSS_INDIR_TBL_UV3P(i), reta); + else if (hw->rss_type == rnpm_rss_uv440) + wr32(hw, RNPM_ETH_RSS_INDIR_TBL(port, i), reta); + else if (hw->rss_type == rnpm_rss_n10) + wr32(hw, RNPM_ETH_RSS_INDIR_TBL(port, i), reta); + } +} + +void rnpm_store_key(struct rnpm_pf_adapter *pf_adapter) +{ + u8 *key = pf_adapter->rss_key; + int key_len = RNPM_RSS_KEY_SIZE; + u8 *key_temp; + int i; + u32 *value; + + key_temp = kmalloc(key_len, GFP_KERNEL); + for (i = 0; i < key_len; i++) + *(key_temp + key_len - i - 1) = *(key + i); + value = (u32 *)key_temp; + for (i = 0; i < key_len; i = i + 4) + rnpm_wr_reg(pf_adapter->hw_addr + RNPM_ETH_RSS_KEY + i, + *(value + i / 4)); + kfree(key_temp); +} + +int rnpm_init_rss_key(struct rnpm_pf_adapter *pf_adapter) +{ + // int i; + //struct rnpm_hw *hw = &adapter->hw; + //struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + unsigned long flags; +// for test only +//#define DEBUG_RSS +#ifdef DEBUG_RSS + u8 temp[] = { 0xca, 0xf9, 0x8f, 0x24, 0xc2, 0x10, 0x50, 0x22, + 0x1f, 0x6c, 0xec, 0xc8, 0xd5, 0x9d, 0x8c, 0xa6, + 0x96, 0x0b, 0x50, 0xf9, 0x24, 0x89, 0x74, 0x96, + 0xf2, 0xbd, 0xbe, 0xbc, 0x5c, 0x81, 0xb2, 0x06, + 0x3d, 0xb4, 0x08, 0x56, 0xca, 0x0c, 0x62, 0x1a }; +#endif + //u32 iov_en = (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + // ? RNPM_IOV_ENABLED : 0; + u32 iov_en = 0; + + /* only init rss key once */ + /* no change rss key if user input one */ + /* get the key */ + spin_lock_irqsave(&pf_adapter->key_setup_lock, flags); + if (!pf_adapter->rss_key_setup_flag) { + //netdev_rss_key_fill(pf_adapter->rss_key, RNPM_RSS_KEY_SIZE); +#ifdef DEBUG_RSS + memcpy(pf_adapter->rss_key, temp, RNPM_RSS_KEY_SIZE); +#else + netdev_rss_key_fill(pf_adapter->rss_key, RNPM_RSS_KEY_SIZE); +#endif + pf_adapter->rss_key_setup_flag = 1; + } + rnpm_store_key(pf_adapter); + /* open rss if rx hash is open ? */ + wr32(pf_adapter, RNPM_ETH_RSS_CONTROL, + RNPM_ETH_ENABLE_RSS_ONLY | iov_en); + spin_unlock_irqrestore(&pf_adapter->key_setup_lock, flags); + + return 0; +} + +int rnpm_init_rss_table(struct rnpm_adapter *adapter) +{ + int rx_nums = adapter->num_rx_queues; + int i, j; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *rx_ring; + u32 reta = 0; + u32 reta_entries = rnpm_rss_indir_tbl_entries(adapter); + u32 port = adapter->port; + + /* adapter->num_q_vectors is not correct */ + for (i = 0, j = 0; i < reta_entries; i++) { + /* init with default value */ + if (!adapter->rss_tbl_setup_flag) + adapter->rss_indir_tbl[i] = j; + /* in sriov mode reta in [0, rx_nums] */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + reta = j; + else { + /* in no sriov, reta is real ring number */ + rx_ring = adapter->rx_ring[adapter->rss_indir_tbl[i]]; + if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) { + /* clean table to zero if rx hash off */ + reta = adapter->port; + } else { + int port_offset = + rd32(hw, RNPM_ETH_TC_PORT_OFFSET_TABLE( + adapter->port)); + /* we use port_offset + rss_table to + * real ring + */ + reta = rx_ring->rnpm_queue_idx - port_offset; + } + } + /* rss table should add ring_offset */ + if (hw->rss_type == rnpm_rss_uv3p) { + wr32(hw, RNPM_ETH_RSS_INDIR_TBL_UV3P(i), reta); + wr32(hw, RNPM_ETH_RSS_MODE, 6); + } else if (hw->rss_type == rnpm_rss_uv440) { + wr32(hw, RNPM_ETH_RSS_INDIR_TBL(port, i), reta); + } else if (hw->rss_type == rnpm_rss_n10) { + wr32(hw, RNPM_ETH_RSS_INDIR_TBL(port, i), reta); + } + j = (j + 1) % rx_nums; + } + /* tbl only init once */ + adapter->rss_tbl_setup_flag = 1; + + for (i = 0, j = 0; i < reta_entries; i++) { + dbg("indir %d table is %d\n", i, adapter->rss_indir_tbl[i]); + if (hw->rss_type == rnpm_rss_uv3p) { + dbg("reg %x is %d\n", RNPM_ETH_RSS_INDIR_TBL_UV3P(i), + rd32(hw, RNPM_ETH_RSS_INDIR_TBL_UV3P(i))); + } else { + dbg("reg %x is %d\n", RNPM_ETH_RSS_INDIR_TBL(port, i), + rd32(hw, RNPM_ETH_RSS_INDIR_TBL(port, i))); + } + } + return 0; +} + +void rnpm_setup_dma_rx(struct rnpm_adapter *adapter, int count_in_dw) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 data; + + data = rd32(hw, RNPM_DMA_CONFIG); + data &= (0x00000ffff); + data |= (count_in_dw << 16); + wr32(hw, RNPM_DMA_CONFIG, data); +} + +void rnpm_setup_layer2_remapping(struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 hw_id, + u8 queue) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 offset = adapter->port; + + drection_dbg("try to eable layer2 %x\n", input->layer2_formate.proto); + /* enable l2 proto setup */ + //rnpm_set_reg_bit(hw, RNPM_ETH_VLAN_FILTER_ENABLE, 31); + /* enable layer2 */ + wr32(hw, RNPM_ETH_LAYER2_ETQF(hw_id), + (0x1 << 31) | (ntohs(input->layer2_formate.proto))); + /* setup action */ + if (queue == RNPM_FDIR_DROP_QUEUE) { + wr32(hw, RNPM_ETH_LAYER2_ETQS(hw_id), (0x1 << 31)); + } else { + /* setup ring_number */ + /* in multiple mode queue must sub port offset */ + wr32(hw, RNPM_ETH_LAYER2_ETQS(hw_id), + (0x1 << 30) | ((queue - offset) << 20)); + } +} + +void rnpm_setup_tuple5_remapping(struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 hw_id, + u8 queue) +{ + u32 port = 0; + u8 mask_temp = 0; + u8 l4_proto_type = 0; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 offset = adapter->port; + + drection_dbg("try to eable tuple 5 %x\n", hw_id); + if (input->formatted.src_ip[0] != 0) + wr32(hw, RNPM_ETH_TUPLE5_SAQF(hw_id), + htonl(input->formatted.src_ip[0])); + else + mask_temp |= RNPM_SRC_IP_MASK; + + if (input->formatted.dst_ip[0] != 0) { + wr32(hw, RNPM_ETH_TUPLE5_DAQF(hw_id), + htonl(input->formatted.dst_ip[0])); + } else + mask_temp |= RNPM_DST_IP_MASK; + + if (input->formatted.src_port != 0) + port |= (htons(input->formatted.src_port)); + else + mask_temp |= RNPM_SRC_PORT_MASK; + + if (input->formatted.dst_port != 0) + port |= (htons(input->formatted.dst_port) << 16); + else + mask_temp |= RNPM_DST_PORT_MASK; + + if (port != 0) + wr32(hw, RNPM_ETH_TUPLE5_SDPQF(hw_id), port); + + switch (input->formatted.flow_type) { + case RNPM_ATR_FLOW_TYPE_TCPV4: + l4_proto_type = IPPROTO_TCP; + break; + case RNPM_ATR_FLOW_TYPE_UDPV4: + l4_proto_type = IPPROTO_UDP; + break; + case RNPM_ATR_FLOW_TYPE_SCTPV4: + l4_proto_type = IPPROTO_SCTP; + break; + case RNPM_ATR_FLOW_TYPE_IPV4: + l4_proto_type = input->formatted.inner_mac[0]; + break; + default: + l4_proto_type = 0; + } + + if (l4_proto_type == 0) + mask_temp |= RNPM_L4_PROTO_MASK; + + /* setup ftqf*/ + /* always set 0x3 */ + wr32(hw, RNPM_ETH_TUPLE5_FTQF(hw_id), + (1 << 31) | (mask_temp << 25) | (l4_proto_type << 16) | 0x3); + + /* setup action */ + if (queue == RNPM_FDIR_DROP_QUEUE) { + wr32(hw, RNPM_ETH_TUPLE5_POLICY(hw_id), (0x1 << 31)); + } else { + /* setup ring_number */ + wr32(hw, RNPM_ETH_TUPLE5_POLICY(hw_id), + ((0x1 << 30) | ((queue - offset) << 20))); + } +} + +void rnpm_setup_tuple5_remapping_tcam(struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 hw_id, + u8 queue) +{ + u32 port = 0; + u32 port_mask = 0; + u8 l4_proto_type = 0; + u8 l4_proto_mask = 0xff; + u32 action = 0; + u32 mark = 0; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + u8 offset = adapter->port; + + wr32(hw, RNPM_TCAM_MODE, 2); + //wr32(hw, RNPM_TCAM_CACHE_ENABLE, 1); + drection_dbg("try to eable tcam %x\n", hw_id); + if (input->formatted.src_ip[0] != 0) { + wr32(hw, RNPM_TCAM_SAQF(hw_id), + htonl(input->formatted.src_ip[0])); + wr32(hw, RNPM_TCAM_SAQF_MASK(hw_id), + htonl(input->formatted.src_ip_mask[0])); + } else { + wr32(hw, RNPM_TCAM_SAQF(hw_id), 0); + wr32(hw, RNPM_TCAM_SAQF_MASK(hw_id), 0); + } + if (input->formatted.dst_ip[0] != 0) { + wr32(hw, RNPM_TCAM_DAQF(hw_id), + htonl(input->formatted.dst_ip[0])); + wr32(hw, RNPM_TCAM_DAQF_MASK(hw_id), + htonl(input->formatted.dst_ip_mask[0])); + } else { + wr32(hw, RNPM_TCAM_DAQF(hw_id), 0); + wr32(hw, RNPM_TCAM_DAQF_MASK(hw_id), 0); + } + if (input->formatted.src_port != 0) { + port |= (htons(input->formatted.src_port) << 16); + port_mask |= (htons(input->formatted.src_port_mask) << 16); + } else { + } + if (input->formatted.dst_port != 0) { + port |= (htons(input->formatted.dst_port)); + port_mask |= (htons(input->formatted.src_port_mask)); + } + + /* setup src & dst port */ + if (port != 0) { + wr32(hw, RNPM_TCAM_SDPQF(hw_id), port); + wr32(hw, RNPM_TCAM_SDPQF_MASK(hw_id), port_mask); + } else { + wr32(hw, RNPM_TCAM_SDPQF(hw_id), 0); + wr32(hw, RNPM_TCAM_SDPQF_MASK(hw_id), 0); + } + + switch (input->formatted.flow_type) { + case RNPM_ATR_FLOW_TYPE_TCPV4: + l4_proto_type = IPPROTO_TCP; + break; + case RNPM_ATR_FLOW_TYPE_UDPV4: + l4_proto_type = IPPROTO_UDP; + break; + case RNPM_ATR_FLOW_TYPE_SCTPV4: + l4_proto_type = IPPROTO_SCTP; + break; + case RNPM_ATR_FLOW_TYPE_IPV4: + l4_proto_type = input->formatted.inner_mac[0]; + l4_proto_mask = input->formatted.inner_mac_mask[0]; + break; + default: + l4_proto_type = 0; + l4_proto_mask = 0; + } + + if (l4_proto_type != 0) { + action |= l4_proto_type; + mark |= l4_proto_mask; + } else { + } + + /* setup action */ + if (queue == RNPM_FDIR_DROP_QUEUE) { + wr32(hw, RNPM_TCAM_APQF(hw_id), (0x1 << 31) | action); + wr32(hw, RNPM_TCAM_APQF_MASK(hw_id), mark); + } else { + /* setup ring_number */ + wr32(hw, RNPM_TCAM_APQF(hw_id), + ((0x1 << 30) | ((queue - offset) << 16) | action)); + wr32(hw, RNPM_TCAM_APQF_MASK(hw_id), mark); + } + wr32(hw, RNPM_TCAM_MODE, 1); +} + +/* setup to the hw */ +s32 rnpm_fdir_write_perfect_filter(int fdir_mode, struct rnpm_hw *hw, + union rnpm_atr_input *filter, u16 hw_id, + u8 queue) +{ + if (filter->formatted.flow_type == RNPM_ATR_FLOW_TYPE_ETHER) { + rnpm_setup_layer2_remapping(hw, filter, hw_id, queue); + } else { + if (fdir_mode != fdir_mode_tcam) + rnpm_setup_tuple5_remapping(hw, filter, hw_id, queue); + else + rnpm_setup_tuple5_remapping_tcam(hw, filter, hw_id, + queue); + } + + return 0; +} + +int rnpm_card_partially_supported_10g_1g_sfp(struct rnpm_pf_adapter *pf_adapter) +{ + if (pf_adapter && (pf_adapter->hw.ablity_speed == SPEED_10000) && + (pf_adapter->adapter_cnt == 2)) { + return 1; + } + + return 0; +} + +s32 rnpm_fdir_erase_perfect_filter(int fdir_mode, struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 hw_id) +{ + /* just disable filter */ + if (input->formatted.flow_type == RNPM_ATR_FLOW_TYPE_ETHER) { + wr32(hw, RNPM_ETH_LAYER2_ETQF(hw_id), 0); + dbg("disable layer2 %d\n", hw_id); + } else { + if (fdir_mode != fdir_mode_tcam) { + wr32(hw, RNPM_ETH_TUPLE5_FTQF(hw_id), 0); + dbg("disable tuple5 %d\n", hw_id); + } else { + /* earase tcam */ + wr32(hw, RNPM_TCAM_MODE, 2); + //wr32(hw, RNPM_TCAM_CACHE_ENABLE, 1); + wr32(hw, RNPM_TCAM_SAQF(hw_id), 0); + wr32(hw, RNPM_TCAM_SAQF_MASK(hw_id), 0); + wr32(hw, RNPM_TCAM_DAQF(hw_id), 0); + wr32(hw, RNPM_TCAM_DAQF_MASK(hw_id), 0); + wr32(hw, RNPM_TCAM_SDPQF(hw_id), 0); + wr32(hw, RNPM_TCAM_SDPQF_MASK(hw_id), 0); + wr32(hw, RNPM_TCAM_APQF(hw_id), 0); + wr32(hw, RNPM_TCAM_APQF_MASK(hw_id), 0); + wr32(hw, RNPM_TCAM_MODE, 1); + /* update tcam cache */ + wr32(hw, RNPM_TCAM_CACHE_ADDR_CLR, 0); + wr32(hw, RNPM_TCAM_CACHE_REQ_CLR, 0); + } + } + + return 0; +} + +u32 rnpm_tx_desc_unused_sw(struct rnpm_ring *tx_ring) +{ + u16 ntu = tx_ring->next_to_use; + u16 ntc = tx_ring->next_to_clean; + u16 count = tx_ring->count; + + return ((ntu >= ntc) ? (count - ntu + ntc) : (ntc - ntu)); +} + +u32 rnpm_rx_desc_used_hw(struct rnpm_hw *hw, struct rnpm_ring *rx_ring) +{ + u32 head = rd32(hw, + RNPM_DMA_REG_RX_DESC_BUF_HEAD(rx_ring->rnpm_queue_idx)); + u32 tail = rd32(hw, + RNPM_DMA_REG_RX_DESC_BUF_TAIL(rx_ring->rnpm_queue_idx)); + u16 count = rx_ring->count; + + return ((tail >= head) ? (count - tail + head) : (head - tail)); +} + +u32 rnpm_tx_desc_unused_hw(struct rnpm_hw *hw, struct rnpm_ring *tx_ring) +{ + u32 head = rd32(hw, + RNPM_DMA_REG_TX_DESC_BUF_HEAD(tx_ring->rnpm_queue_idx)); + u32 tail = rd32(hw, + RNPM_DMA_REG_TX_DESC_BUF_TAIL(tx_ring->rnpm_queue_idx)); + u16 count = tx_ring->count; + + return ((tail >= head) ? (count - tail + head) : (head - tail)); +} + +s32 rnpm_disable_rxr_maxrate(struct net_device *netdev, u8 queue_index) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *rx_ring = adapter->rx_ring[queue_index]; + u32 reg_idx = rx_ring->rnpm_queue_idx; + + /* disable which dma ring in maxrate limit mode */ + wr32(hw, RNPM_SELECT_RING_EN(reg_idx), 0); + /* Clear Tx Ring maxrate */ + wr32(hw, RNPM_RX_RING_MAXRATE(reg_idx), 0); + + return 0; +} + +s32 rnpm_enable_rxr_maxrate(struct net_device *netdev, u8 queue_index, + u32 maxrate) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *rx_ring = adapter->rx_ring[queue_index]; + u32 reg_idx = rx_ring->rnpm_queue_idx; + u32 real_rate = maxrate / 16; + + if (!real_rate) + return -EINVAL; + + wr32(hw, RNPM_RING_FC_ENABLE, true); + /* disable which dma ring in maxrate limit mode */ + wr32(hw, RNPM_SELECT_RING_EN(reg_idx), true); + /* Clear Tx Ring maxrate */ + wr32(hw, RNPM_RX_RING_MAXRATE(reg_idx), real_rate); + + return 0; +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_main.c b/drivers/net/ethernet/mucse/rnpm/rnpm_main.c new file mode 100644 index 000000000000..98a27c032ed9 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_main.c @@ -0,0 +1,9250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rnpm.h" +#include "rnpm_common.h" +#include "rnpm_sriov.h" +#include "rnpm_ptp.h" +#include "rnpm_tc.h" +#include "rnpm_mbx.h" +#include "rnpm_mbx_fw.h" +#include "version.h" +#include "rnpm_mpe.h" +#include +#include +#include + +// for test +#ifdef CONFIG_ARM64 +#define NO_BQL_TEST +#endif + +#define TX_IRQ_MISS_REDUCE + +char rnpm_driver_name[] = "rnpm"; +char rnpm_port_name[] = "enp"; + +#ifndef NO_NETDEV_PORT +#define ASSIN_PDEV +#endif +static const char rnpm_driver_string[] = + "mucse 4/8port 1/10 Gigabit PCI Express Network Driver"; +static char rnpm_default_device_descr[] __maybe_unused = + "mucse(R) 4/8port 1/10 Gigabit Network Connection"; +#define DRV_VERSION "0.3.1.rc4" +const char rnpm_driver_version[] = DRV_VERSION GIT_COMMIT; +static const char rnpm_copyright[] = + "Copyright (c) 2022-2024 mucse Corporation."; + +static struct rnpm_info *rnpm_info_tbl[] = { + [board_n10] = &rnpm_n10_info, + [board_n400_4x1G] = &rnpm_n400_4x1G_info, +}; + +static void rnpm_pull_tail(struct sk_buff *skb); +#ifdef RNPM_OPTM_WITH_LPAGE +static bool rnpm_alloc_mapped_page(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *bi, + union rnpm_rx_desc *rx_desc, u16 bufsz, + u64 fun_id); +static void rnpm_put_rx_buffer(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer); + +#else +static bool rnpm_alloc_mapped_page(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *bi); +static void rnpm_put_rx_buffer(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + struct sk_buff *skb); +#endif + +// #define DEBUG_TX + +// vu440 must select mode type +#ifdef UV440_2PF +#ifdef MODE_4_PORT +#define MODE_TYPE board_vu440_8x10G +#endif + +#ifdef MODE_2_PORT +#define MODE_TYPE board_vu440_4x10G +#endif + +#ifdef MODE_1_PORT +#define MODE_TYPE board_vu440_2x10G +#endif + +#ifndef MODE_TYPE +/* default in 4 ports in 1 pf mode */ +#define MODE_TYPE board_vu440_8x10G +#endif +#endif +/* itr can be modified in napi handle */ +/* now hw not support this */ + +static struct pci_device_id rnpm_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1060), .driver_data = board_n10 }, + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1C60), .driver_data = board_n10 }, + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1020), .driver_data = board_n10 }, + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1C20), .driver_data = board_n10 }, + + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1021), + .driver_data = board_n400_4x1G }, + { PCI_DEVICE(PCI_VENDOR_ID_MUCSE, 0x1c21), + .driver_data = board_n400_4x1G }, + + /* required last entry */ + { + 0, + }, +}; + +MODULE_DEVICE_TABLE(pci, rnpm_pci_tbl); + +static unsigned int mac_loop_en; +module_param(mac_loop_en, uint, 0000); + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0000); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +static unsigned int pf_msix_counts_set; +module_param(pf_msix_counts_set, uint, 0000); +MODULE_PARM_DESC(pf_msix_counts_set, "set msix count by one pf"); + +/* just for test */ +static unsigned int fix_eth_name; +module_param(fix_eth_name, uint, 0000); +MODULE_PARM_DESC(fix_eth_name, "set eth adapter name to rnpmXX"); + +#ifndef NO_PTP +static int module_enable_ptp = 1; +module_param(module_enable_ptp, uint, 0000); +MODULE_PARM_DESC(module_enable_ptp, "enable ptp feature, disabled default"); +#endif + +unsigned int mpe_src_port; +module_param(mpe_src_port, uint, 0000); +MODULE_PARM_DESC(mpe_src_port, "mpe src port"); + +unsigned int mpe_pkt_version; +module_param(mpe_pkt_version, uint, 0000); +MODULE_PARM_DESC(mpe_pkt_version, "ipv4 or ipv6 src port"); + +static int port_valid_pf0 = 0xf; +module_param(port_valid_pf0, uint, 0000); +MODULE_PARM_DESC(port_valid_pf0, "pf0 valid (only in 8 ports mode"); + +static int port_valid_pf1 = 0xf; +module_param(port_valid_pf1, uint, 0000); +MODULE_PARM_DESC(port_valid_pf1, "pf1 valid (only in 8 ports mode"); + +static unsigned int port_names_pf0 = 0x03020100; +module_param(port_names_pf0, uint, 0000); +MODULE_PARM_DESC(port_names_pf0, "pf0 names (only in 8 ports mode"); + +static unsigned int port_names_pf1 = 0x03020100; +module_param(port_names_pf1, uint, 0000); +MODULE_PARM_DESC(port_names_pf1, "pf1 names (only in 8 ports mode"); + +static int fw_10g_1g_auto_det; +module_param(fw_10g_1g_auto_det, uint, 0000); + +static int force_speed_ablity_pf0; +module_param(force_speed_ablity_pf0, uint, 0000); +MODULE_PARM_DESC(force_speed_ablity_pf0, + "allow to force speed 1/10G for fiber on pf0"); + +static int force_speed_ablity_pf1; +module_param(force_speed_ablity_pf1, uint, 0000); +MODULE_PARM_DESC(force_speed_ablity_pf1, + "allow to force speed 1/10G for fiber on pf1"); + +MODULE_PARM_DESC( + fw_10g_1g_auto_det, + "enable 4x10G cards partially supported 10G and 1G SFP at the same time "); + +MODULE_AUTHOR("Mucse Corporation, "); +MODULE_DESCRIPTION("Mucse(R) 1/10 Gigabit PCI Express Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static int enable_hi_dma; + +#if (PAGE_SIZE < 8192) +#define RNPM_MAX_2K_FRAME_BUILD_SKB (RNPM_RXBUFFER_1536 - NET_IP_ALIGN) +#define RNPM_2K_TOO_SMALL_WITH_PADDING \ + ((NET_SKB_PAD + RNPM_RXBUFFER_1536) > \ + SKB_WITH_OVERHEAD(RNPM_RXBUFFER_2K)) + +static inline int rnpm_compute_pad(int rx_buf_len) +{ + int page_size, pad_size; + + page_size = ALIGN(rx_buf_len, PAGE_SIZE / 2); + pad_size = SKB_WITH_OVERHEAD(page_size) - rx_buf_len; + + return pad_size; +} + +static inline int rnpm_skb_pad(void) +{ + int rx_buf_len; + + /* If a 2K buffer cannot handle a standard Ethernet frame then + * optimize padding for a 3K buffer instead of a 1.5K buffer. + * + * For a 3K buffer we need to add enough padding to allow for + * tailroom due to NET_IP_ALIGN possibly shifting us out of + * cache-line alignment. + */ + if (RNPM_2K_TOO_SMALL_WITH_PADDING) + rx_buf_len = RNPM_RXBUFFER_3K + SKB_DATA_ALIGN(NET_IP_ALIGN); + else + rx_buf_len = RNPM_RXBUFFER_1536; + + /* if needed make room for NET_IP_ALIGN */ + rx_buf_len -= NET_IP_ALIGN; + return rnpm_compute_pad(rx_buf_len); +} + +#define RNPM_SKB_PAD rnpm_skb_pad() +#else +#define RNPM_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#endif + +static inline unsigned int rnpm_rx_offset(struct rnpm_ring *rx_ring) +{ + return ring_uses_build_skb(rx_ring) ? RNPM_SKB_PAD : 0; +} + +void rnpm_service_event_schedule(struct rnpm_adapter *adapter) +{ + if (!test_bit(__RNPM_DOWN, &adapter->state) && + !test_and_set_bit(__RNPM_SERVICE_SCHED, &adapter->state)) { + schedule_work(&adapter->service_task); + adapter->service_count++; + } +} + +void rnpm_pf_service_event_schedule(struct rnpm_pf_adapter *pf_adapter) +{ + schedule_work(&pf_adapter->service_task); +} + +static void rnpm_service_event_complete(struct rnpm_adapter *adapter) +{ + BUG_ON(!test_bit(__RNPM_SERVICE_SCHED, &adapter->state)); + + /* flush memory to make sure state is correct before next watchdog */ + // smp_mb__before_clear_bit(); + clear_bit(__RNPM_SERVICE_SCHED, &adapter->state); +} + +void rnpm_release_hw_control(struct rnpm_adapter *adapter) +{ + // u32 ctrl_ext; + + /* Let firmware take over control of h/w */ + // ctrl_ext = RNPM_READ_REG(&adapter->hw, RNPM_CTRL_EXT); + // RNPM_WRITE_REG(&adapter->hw, RNPM_CTRL_EXT, + // ctrl_ext & ~RNPM_CTRL_EXT_DRV_LOAD); +} + +void rnpm_get_hw_control(struct rnpm_adapter *adapter) +{ + // u32 ctrl_ext; + + /* Let firmware know the driver has taken over */ +} + +/** + * rnpm_set_ivar - set the ring_vector registers, + * mapping interrupt causes to vectors + * @adapter: pointer to adapter struct + * @queue: queue to map the corresponding interrupt to + * @msix_vector: the vector to map to the corresponding queue + * + */ +static void rnpm_set_ring_vector(struct rnpm_adapter *adapter, u8 rnpm_queue, + u8 rnpm_msix_vector) +{ + struct rnpm_hw *hw = &adapter->hw; + // struct net_device *netdev = adapter->netdev; + u32 data = 0; + + data = hw->pfvfnum << 24; + data |= (rnpm_msix_vector << 8); + data |= (rnpm_msix_vector << 0); + + DPRINTK(IFUP, INFO, + "Set Ring-Vector queue:%d (reg:0x%x) <-- Rx-MSIX:%d, Tx-MSIX:%d\n", + rnpm_queue, RING_VECTOR(rnpm_queue), rnpm_msix_vector, + rnpm_msix_vector); + + rnpm_wr_reg(hw->ring_msix_base + RING_VECTOR(rnpm_queue), data); +} + +static inline void rnpm_irq_rearm_queues(struct rnpm_adapter *adapter, + u64 qmask) +{ + // u32 mask; +} + +void rnpm_unmap_and_free_tx_resource(struct rnpm_ring *ring, + struct rnpm_tx_buffer *tx_buffer) +{ + if (tx_buffer->skb) { + dev_kfree_skb_any(tx_buffer->skb); + if (dma_unmap_len(tx_buffer, len)) + dma_unmap_single(ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } else if (dma_unmap_len(tx_buffer, len)) { + dma_unmap_page(ring->dev, dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); + } + tx_buffer->next_to_watch = NULL; + tx_buffer->skb = NULL; + dma_unmap_len_set(tx_buffer, len, 0); + /* tx_buffer must be completely set up in the transmit path */ +} + +static u64 rnpm_get_tx_completed(struct rnpm_ring *ring) +{ + return ring->stats.packets; +} + +static u64 rnpm_get_tx_pending(struct rnpm_ring *ring) +{ + struct rnpm_adapter *adapter = netdev_priv(ring->netdev); + struct rnpm_hw *hw = &adapter->hw; + + u32 head = + rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD(ring->rnpm_queue_idx)); + u32 tail = + rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL(ring->rnpm_queue_idx)); + + if (head != tail) + return (head < tail) ? tail - head : + (tail + ring->count - head); + + return 0; +} + +static inline bool rnpm_check_tx_hang(struct rnpm_ring *tx_ring) +{ + u32 tx_done = rnpm_get_tx_completed(tx_ring); + u32 tx_done_old = tx_ring->tx_stats.tx_done_old; + u32 tx_pending = rnpm_get_tx_pending(tx_ring); + bool ret = false; + + clear_check_for_tx_hang(tx_ring); + + /* Check for a hung queue, but be thorough. This verifies + * that a transmit has been completed since the previous + * check AND there is at least one packet pending. The + * ARMED bit is set to indicate a potential hang. The + * bit is cleared if a pause frame is received to remove + * false hang detection due to PFC or 802.3x frames. By + * requiring this to fail twice we avoid races with + * pfc clearing the ARMED bit and conditions where we + * run the check_tx_hang logic with a transmit completion + * pending but without time to complete it yet. + */ + if ((tx_done_old == tx_done) && tx_pending) { + /* make sure it is true for two checks in a row */ + ret = test_and_set_bit(__RNPM_HANG_CHECK_ARMED, + &tx_ring->state); + } else { + /* update completed stats and continue */ + tx_ring->tx_stats.tx_done_old = tx_done; + /* reset the countdown */ + clear_bit(__RNPM_HANG_CHECK_ARMED, &tx_ring->state); + } + + return ret; +} + +/** + * rnpm_tx_timeout_reset - initiate reset due to Tx timeout + * @adapter: driver private struct + **/ +static void rnpm_tx_timeout_reset(struct rnpm_adapter *adapter) +{ + /* Do the reset outside of interrupt context */ + if (!test_bit(__RNPM_DOWN, &adapter->state)) { + // adapter->flags2 |= RNPM_FLAG2_RESET_REQUESTED; + set_bit(RNPM_PF_RESET, &adapter->pf_adapter->flags); + e_warn(drv, "initiating reset due to tx timeout\n"); + rnpm_dbg("initiating reset due to tx timeout\n"); + // rnpm_service_event_schedule(adapter); + } +} + +static void rnpm_check_restart_tx(struct rnpm_q_vector *q_vector, + struct rnpm_ring *tx_ring) +{ + struct rnpm_adapter *adapter = q_vector->adapter; + +#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) + if (likely(netif_carrier_ok(tx_ring->netdev) && + (rnpm_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (__netif_subqueue_stopped(tx_ring->netdev, + tx_ring->queue_index) && + !test_bit(__RNPM_DOWN, &adapter->state)) { + netif_wake_subqueue(tx_ring->netdev, + tx_ring->queue_index); + ++tx_ring->tx_stats.restart_queue; + } + } +} + +/** + * rnpm_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information + * @tx_ring: tx ring to clean + **/ +static bool rnpm_clean_tx_irq(struct rnpm_q_vector *q_vector, + struct rnpm_ring *tx_ring, int napi_budget) +{ + struct rnpm_adapter *adapter = q_vector->adapter; + struct rnpm_tx_buffer *tx_buffer; + struct rnpm_tx_desc *tx_desc; + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = q_vector->tx.work_limit; + unsigned int i = tx_ring->next_to_clean; + + if (test_bit(__RNPM_DOWN, &adapter->state)) + return true; + tx_ring->tx_stats.poll_count++; + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = RNPM_TX_DESC(tx_ring, i); + i -= tx_ring->count; + + do { + struct rnpm_tx_desc *eop_desc = tx_buffer->next_to_watch; + + /* if next_to_watch is not set then there is no work pending */ + if (!eop_desc) + break; + + /* prevent any other reads prior to eop_desc */ + // read_barrier_depends(); + smp_rmb(); + + /* if eop DD is not set pending work has not been completed */ + if (!(eop_desc->vlan_cmd & cpu_to_le32(RNPM_TXD_STAT_DD))) + break; + + /* clear next_to_watch to prevent false hangs */ + tx_buffer->next_to_watch = NULL; + + /* update the statistics for this packet */ + total_bytes += tx_buffer->bytecount; + total_packets += tx_buffer->gso_segs; + + /* free the skb */ + napi_consume_skb(tx_buffer->skb, napi_budget); + /* unmap skb header data */ + dma_unmap_single(tx_ring->dev, dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); + + /* clear tx_buffer data */ + tx_buffer->skb = NULL; + dma_unmap_len_set(tx_buffer, len, 0); + + /* unmap remaining buffers */ + while (tx_desc != eop_desc) { + /* print desc */ + buf_dump_line("desc %d ", i + tx_ring->count, tx_desc, + sizeof(*tx_desc)); + + tx_buffer++; + tx_desc++; + i++; + if (unlikely(!i)) { + i -= tx_ring->count; + tx_buffer = tx_ring->tx_buffer_info; + tx_desc = RNPM_TX_DESC(tx_ring, 0); + } + + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buffer, len)) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer, len, 0); + } + } + + /* move us one more past the eop_desc for start of next pkt */ + tx_buffer++; + tx_desc++; + i++; + if (unlikely(!i)) { + i -= tx_ring->count; + tx_buffer = tx_ring->tx_buffer_info; + tx_desc = RNPM_TX_DESC(tx_ring, 0); + } + + /* issue prefetch for next Tx descriptor */ + prefetch(tx_desc); + + /* update budget accounting */ + budget--; + } while (likely(budget)); + + i += tx_ring->count; + tx_ring->next_to_clean = i; + u64_stats_update_begin(&tx_ring->syncp); + tx_ring->stats.bytes += total_bytes; + tx_ring->stats.packets += total_packets; + u64_stats_update_end(&tx_ring->syncp); + q_vector->tx.total_bytes += total_bytes; + q_vector->tx.total_packets += total_packets; + tx_ring->tx_stats.send_done_bytes += total_bytes; + +#ifdef NO_BQL_TEST +#else + netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, + total_bytes); +#endif +#ifndef TX_IRQ_MISS_REDUCE +#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) + if (likely(netif_carrier_ok(tx_ring->netdev) && + (rnpm_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (__netif_subqueue_stopped(tx_ring->netdev, + tx_ring->queue_index) && + !test_bit(__RNPM_DOWN, &adapter->state)) { + netif_wake_subqueue(tx_ring->netdev, + tx_ring->queue_index); + ++tx_ring->tx_stats.restart_queue; + } + } +#endif + + return !!budget; +} + +static inline void rnpm_rx_hash(struct rnpm_ring *ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + int rss_type; + + if (!(ring->netdev->features & NETIF_F_RXHASH)) + return; +#define RNPM_RSS_TYPE_MASK 0xc0 + rss_type = rx_desc->wb.cmd & RNPM_RSS_TYPE_MASK; + skb_set_hash(skb, le32_to_cpu(rx_desc->wb.rss_hash), + rss_type ? PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); +} + +/** + * rnpm_rx_checksum - indicate in skb if hw indicated a good cksum + * @ring: structure containing ring specific data + * @rx_desc: current Rx descriptor being processed + * @skb: skb currently being received and modified + **/ +static inline void rnpm_rx_checksum(struct rnpm_ring *ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + bool encap_pkt = false; + + skb_checksum_none_assert(skb); + /* Rx csum disabled */ + if (!(ring->netdev->features & NETIF_F_RXCSUM)) + return; + + /* vxlan packet handle ? */ + if (rnpm_get_stat(rx_desc, RNPM_RXD_STAT_TUNNEL_MASK) == + RNPM_RXD_STAT_TUNNEL_VXLAN) { + encap_pkt = true; + skb->encapsulation = 1; + skb->ip_summed = CHECKSUM_NONE; + } + + /* if outer L3/L4 error */ + /* must in promisc mode */ + if (rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_ERR_MASK) && + !ignore_veb_pkg_err(ring->q_vector->adapter, rx_desc)) { + // ring->rx_stats.csum_err++; + return; + } + + ring->rx_stats.csum_good++; + /* at least it is a ip packet which has ip checksum */ + + /* It must be a TCP or UDP packet with a valid checksum */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (encap_pkt) { +#ifdef HAVE_SKBUFF_CSUM_LEVEL + /* If we checked the outer header let the stack know */ + skb->csum_level = 1; +#endif /* HAVE_SKBUFF_CSUM_LEVEL */ + } +} + +static inline void rnpm_update_rx_tail(struct rnpm_ring *rx_ring, u32 val) +{ + rx_ring->next_to_use = val; + /* update next to alloc since we have filled the ring */ + rx_ring->next_to_alloc = val; + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + */ + wmb(); + rnpm_wr_reg(rx_ring->tail, val); +} + +#ifdef RNPM_OPTM_WITH_LPAGE +/** + * rnpm_alloc_rx_buffers - Replace used receive buffers + * @rx_ring: ring to place buffers on + * @cleaned_count: number of buffers to replace + **/ +void rnpm_alloc_rx_buffers(struct rnpm_ring *rx_ring, u16 cleaned_count) +{ + union rnpm_rx_desc *rx_desc; + struct rnpm_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + u64 fun_id = ((u64)(rx_ring->pfvfnum) << (32 + 24)); + u16 bufsz; + /* nothing to do */ + if (!cleaned_count) + return; + + rx_desc = RNPM_RX_DESC(rx_ring, i); + + BUG_ON(rx_desc == NULL); + + bi = &rx_ring->rx_buffer_info[i]; + + BUG_ON(bi == NULL); + + i -= rx_ring->count; + bufsz = rnpm_rx_bufsz(rx_ring); + + do { + int count = 1; + struct page *page; + + // alloc page and init first rx_desc + if (!rnpm_alloc_mapped_page(rx_ring, bi, rx_desc, bufsz, + fun_id)) + break; + page = bi->page; + + rx_desc->resv_cmd = 0; + + rx_desc++; + i++; + bi++; + + if (unlikely(!i)) { + rx_desc = RNPM_RX_DESC(rx_ring, 0); + bi = rx_ring->rx_buffer_info; + i -= rx_ring->count; + } + + rx_desc->resv_cmd = 0; + + cleaned_count--; + + while (count < rx_ring->rx_page_buf_nums && cleaned_count) { + // dma_addr_t dma = bi->dma; + dma_addr_t dma; + + bi->page_offset = rx_ring->rx_per_buf_mem * count + + rnpm_rx_offset(rx_ring); + /* map page for use */ + dma = dma_map_page_attrs(rx_ring->dev, page, + bi->page_offset, bufsz, + DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + + if (dma_mapping_error(rx_ring->dev, dma)) { + rx_ring->rx_stats.alloc_rx_page_failed++; + break; + } + + bi->dma = dma; + bi->page = page; + + page_ref_add(page, USHRT_MAX); + bi->pagecnt_bias = USHRT_MAX; + + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, + 0, bufsz, + DMA_FROM_DEVICE); + + /* Refresh the desc even if buffer_addrs didn't change + * because each write-back erases this info. + */ + rx_desc->pkt_addr = cpu_to_le64(bi->dma + fun_id); + /* clean dd */ + rx_desc->resv_cmd = 0; + + rx_desc++; + bi++; + i++; + if (unlikely(!i)) { + rx_desc = RNPM_RX_DESC(rx_ring, 0); + bi = rx_ring->rx_buffer_info; + i -= rx_ring->count; + } + count++; + /* clear the hdr_addr for the next_to_use descriptor */ + // rx_desc->cmd = 0; + cleaned_count--; + } + } while (cleaned_count); + + i += rx_ring->count; + + if (rx_ring->next_to_use != i) + rnpm_update_rx_tail(rx_ring, i); +} +#else +/** + * rnpm_alloc_rx_buffers - Replace used receive buffers + * @rx_ring: ring to place buffers on + * @cleaned_count: number of buffers to replace + **/ +void rnpm_alloc_rx_buffers(struct rnpm_ring *rx_ring, u16 cleaned_count) +{ + union rnpm_rx_desc *rx_desc; + struct rnpm_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + u64 fun_id = ((u64)(rx_ring->pfvfnum) << (32 + 24)); + u16 bufsz; + /* nothing to do */ + if (!cleaned_count) + return; + + rx_desc = RNPM_RX_DESC(rx_ring, i); + BUG_ON(rx_desc == NULL); + bi = &rx_ring->rx_buffer_info[i]; + BUG_ON(bi == NULL); + i -= rx_ring->count; + bufsz = rnpm_rx_bufsz(rx_ring); + + do { + if (!rnpm_alloc_mapped_page(rx_ring, bi)) + break; + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, + bi->page_offset, bufsz, + DMA_FROM_DEVICE); + + rx_desc->pkt_addr = + cpu_to_le64(bi->dma + bi->page_offset + fun_id); + rx_desc->resv_cmd = 0; + + rx_desc++; + bi++; + i++; + if (unlikely(!i)) { + rx_desc = RNPM_RX_DESC(rx_ring, 0); + bi = rx_ring->rx_buffer_info; + i -= rx_ring->count; + } + + /* clear the hdr_addr for the next_to_use descriptor */ + // rx_desc->cmd = 0; + cleaned_count--; + } while (cleaned_count); + + i += rx_ring->count; + + if (rx_ring->next_to_use != i) + rnpm_update_rx_tail(rx_ring, i); +} +#endif +/** + * rnpm_get_headlen - determine size of header for RSC/LRO/GRO/FCOE + * @data: pointer to the start of the headers + * @max_len: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + **/ +__maybe_unused static unsigned int rnpm_get_headlen(unsigned char *data, + unsigned int max_len) +{ + union { + unsigned char *network; + /* l2 headers */ + struct ethhdr *eth; + struct vlan_hdr *vlan; + /* l3 headers */ + struct iphdr *ipv4; + struct ipv6hdr *ipv6; + } hdr; + __be16 protocol; + u8 nexthdr = 0; /* default to not TCP */ + u8 hlen; + + /* this should never happen, but better safe than sorry */ + if (max_len < ETH_HLEN) + return max_len; + + /* initialize network frame pointer */ + hdr.network = data; + + /* set first protocol and move network header forward */ + protocol = hdr.eth->h_proto; + hdr.network += ETH_HLEN; + + /* handle any vlan tag if present */ + if (protocol == htons(ETH_P_8021Q)) { + if ((hdr.network - data) > (max_len - VLAN_HLEN)) + return max_len; + + protocol = hdr.vlan->h_vlan_encapsulated_proto; + hdr.network += VLAN_HLEN; + } + + /* handle L3 protocols */ + if (protocol == htons(ETH_P_IP)) { + if ((hdr.network - data) > (max_len - sizeof(struct iphdr))) + return max_len; + + /* access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (hdr.network[0] & 0x0F) << 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return hdr.network - data; + + /* record next protocol if header is present */ + if (!(hdr.ipv4->frag_off & htons(IP_OFFSET))) + nexthdr = hdr.ipv4->protocol; + } else if (protocol == htons(ETH_P_IPV6)) { + if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr))) + return max_len; + + /* record next protocol */ + nexthdr = hdr.ipv6->nexthdr; + hlen = sizeof(struct ipv6hdr); + } else { + return hdr.network - data; + } + + /* relocate pointer to start of L4 header */ + hdr.network += hlen; + + /* finally sort out TCP/UDP */ + if (nexthdr == IPPROTO_TCP) { + if ((hdr.network - data) > (max_len - sizeof(struct tcphdr))) + return max_len; + + /* access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (hdr.network[12] & 0xF0) >> 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return hdr.network - data; + + hdr.network += hlen; + } else if (nexthdr == IPPROTO_UDP) { + if ((hdr.network - data) > (max_len - sizeof(struct udphdr))) + return max_len; + + hdr.network += sizeof(struct udphdr); + } + + /* If everything has gone correctly hdr.network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((hdr.network - data) < max_len) + return hdr.network - data; + else + return max_len; +} + +static void rnpm_set_rsc_gso_size(struct rnpm_ring *ring, struct sk_buff *skb) +{ + u16 hdr_len = skb_headlen(skb); + + /* set gso_size to avoid messing up TCP MSS */ + skb_shinfo(skb)->gso_size = + DIV_ROUND_UP((skb->len - hdr_len), RNPM_CB(skb)->append_cnt); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; +} + +__maybe_unused static void rnpm_update_rsc_stats(struct rnpm_ring *rx_ring, + struct sk_buff *skb) +{ + /* if append_cnt is 0 then frame is not RSC */ + if (!RNPM_CB(skb)->append_cnt) + return; + + rx_ring->rx_stats.rsc_count += RNPM_CB(skb)->append_cnt; + rx_ring->rx_stats.rsc_flush++; + + rnpm_set_rsc_gso_size(rx_ring, skb); + + /* gso_size is computed using append_cnt so always clear it last */ + RNPM_CB(skb)->append_cnt = 0; +} +static void rnpm_rx_vlan(struct rnpm_ring *rx_ring, union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + if ((netdev_ring(rx_ring)->features & NETIF_F_HW_VLAN_CTAG_RX) && + rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_VLAN_VALID)) { + rx_ring->rx_stats.vlan_remove++; + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rx_desc->wb.vlan)); + } +} +/** + * rnpm_process_skb_fields - Populate skb header fields from Rx descriptor + * @rx_ring: rx descriptor ring packet is being transacted on + * @rx_desc: pointer to the EOP Rx descriptor + * @skb: pointer to current skb being populated + * + * This function checks the ring, descriptor, and packet information in + * order to populate the hash, checksum, VLAN, timestamp, protocol, and + * other fields within the skb. + **/ +static void rnpm_process_skb_fields(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + struct net_device *dev = rx_ring->netdev; + + rnpm_rx_hash(rx_ring, rx_desc, skb); + rnpm_rx_checksum(rx_ring, rx_desc, skb); + rnpm_rx_vlan(rx_ring, rx_desc, skb); + skb_record_rx_queue(skb, rx_ring->queue_index); + skb->protocol = eth_type_trans(skb, dev); +} + +static void rnpm_rx_skb(struct rnpm_q_vector *q_vector, struct sk_buff *skb) +{ + napi_gro_receive(&q_vector->napi, skb); +} + +#ifdef RNPM_OPTM_WITH_LPAGE +/** + * rnp_is_non_eop - process handling of non-EOP buffers + * @rx_ring: Rx ring being processed + * @rx_desc: Rx descriptor for current buffer + * @skb: Current socket buffer containing buffer in progress + * + * This function updates next to clean. If the buffer is an EOP buffer + * this function exits returning false, otherwise it will place the + * sk_buff in the next buffer to be chained and return true indicating + * that this is in fact a non-EOP buffer. + **/ +static bool rnpm_is_non_eop(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc) +{ + u32 ntc = rx_ring->next_to_clean + 1; + /* fetch, update, and store next to clean */ + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + + prefetch(RNPM_RX_DESC(rx_ring, ntc)); + + /* if we are the last buffer then there is nothing else to do */ + if (likely(rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_EOP))) + return false; + + rx_ring->rx_stats.non_eop_descs++; + return true; +} +#else +/** + * rnpm_is_non_eop - process handling of non-EOP buffers + * @rx_ring: Rx ring being processed + * @rx_desc: Rx descriptor for current buffer + * @skb: Current socket buffer containing buffer in progress + * + * This function updates next to clean. If the buffer is an EOP buffer + * this function exits returning false, otherwise it will place the + * sk_buff in the next buffer to be chained and return true indicating + * that this is in fact a non-EOP buffer. + **/ +static bool rnpm_is_non_eop(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, struct sk_buff *skb) +{ + u32 ntc = rx_ring->next_to_clean + 1; + /* fetch, update, and store next to clean */ + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + + prefetch(RNPM_RX_DESC(rx_ring, ntc)); + + /* if we are the last buffer then there is nothing else to do */ + if (likely(rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_EOP))) + return false; + /* place skb in next buffer to be received */ + rx_ring->rx_buffer_info[ntc].skb = skb; + rx_ring->rx_stats.non_eop_descs++; + + return true; +} +#endif + +/* drop this packets if error */ +static bool rnpm_check_csum_error(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, + unsigned int size, + unsigned int *driver_drop_packets) +{ + bool err = false; + + struct net_device *netdev = rx_ring->netdev; + + if (netdev->features & NETIF_F_RXCSUM) { + if (unlikely(rnpm_test_staterr(rx_desc, + RNPM_RXD_STAT_ERR_MASK))) { + rx_debug_printk("rx error: VEB:%s mark:0x%x cmd:0x%x\n", + (rx_ring->q_vector->adapter->flags & + RNPM_FLAG_SRIOV_ENABLED) ? + "On" : + "Off", + rx_desc->wb.mark, rx_desc->wb.cmd); + /* push this packet to stack if in promisc mode */ + rx_ring->rx_stats.csum_err++; + + if ((!(netdev->flags & IFF_PROMISC) && + (!(netdev->features & NETIF_F_RXALL)))) { + // if not ipv4 with l4 error, we should ignore l4 csum error + if (unlikely(rnpm_test_staterr( + rx_desc, + RNPM_RXD_STAT_L4_MASK) && + (!(rx_desc->wb.rev1 & + RNPM_RX_L3_TYPE_MASK)))) { + rx_ring->rx_stats.csum_err--; + goto skip_fix; + } + + if (unlikely(rnpm_test_staterr( + rx_desc, + RNPM_RXD_STAT_SCTP_MASK))) { + if ((size > 60) && + (rx_desc->wb.rev1 & + RNPM_RX_L3_TYPE_MASK)) { + err = true; + } else { + /* sctp less than 60 hw report err by mistake */ + rx_ring->rx_stats.csum_err--; + } + } else { + err = true; + } + } + } + } + +skip_fix: + if (err) { + u32 ntc = rx_ring->next_to_clean + 1; + struct rnpm_rx_buffer *rx_buffer; +#if (PAGE_SIZE < 8192) + unsigned int truesize = rnpm_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = + ring_uses_build_skb(rx_ring) ? + SKB_DATA_ALIGN(RNPM_SKB_PAD + size) : + SKB_DATA_ALIGN(size); +#endif + + // if eop add drop_packets + if (likely(rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_EOP))) + *driver_drop_packets = *driver_drop_packets + 1; + + /* we are reusing so sync this buffer for CPU use */ + rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, + rx_buffer->page_offset, + RNPM_RXBUFFER_1536, + DMA_FROM_DEVICE); + + // rx_buffer->pagecnt_bias--; + +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + +#ifdef RNPM_OPTM_WITH_LPAGE + rnpm_put_rx_buffer(rx_ring, rx_buffer); +#else + rnpm_put_rx_buffer(rx_ring, rx_buffer, NULL); +#endif + // update to the next desc + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + } + + return err; +} + +/** + * rnpm_rx_ring_reinit - just reinit rx_ring with new count in ->reset_count + * @rx_ring: rx descriptor ring to transact packets on + */ +int rnpm_rx_ring_reinit(struct rnpm_adapter *adapter, struct rnpm_ring *rx_ring) +{ + struct rnpm_ring *temp_ring = NULL; + int err = 0; + struct rnpm_hw *hw = &adapter->hw; + + temp_ring = vmalloc(array_size(1, sizeof(struct rnpm_ring))); + if (!temp_ring) + return -1; + + if (rx_ring->count == rx_ring->reset_count) + return 0; + /* stop rx queue */ + + rnpm_disable_rx_queue(adapter, rx_ring); + memset(temp_ring, 0x00, sizeof(struct rnpm_ring)); + /* reinit for this ring */ + memcpy(temp_ring, rx_ring, sizeof(struct rnpm_ring)); + /* setup new count */ + temp_ring->count = rx_ring->reset_count; + err = rnpm_setup_rx_resources(temp_ring, adapter); + if (err) { + rnpm_free_rx_resources(temp_ring); + goto err_setup; + } + rnpm_free_rx_resources(rx_ring); + memcpy(rx_ring, temp_ring, sizeof(struct rnpm_ring)); + rnpm_configure_rx_ring(adapter, rx_ring); +err_setup: + /* start rx */ + wr32(hw, RNPM_DMA_RX_START(rx_ring->rnpm_queue_idx), 1); + vfree(temp_ring); + return 0; +} + +#ifdef RNPM_OPTM_WITH_LPAGE +static bool rnpm_alloc_mapped_page(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *bi, + union rnpm_rx_desc *rx_desc, u16 bufsz, + u64 fun_id) +{ + struct page *page = bi->page; + dma_addr_t dma; + + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) + return true; + + page = dev_alloc_pages(RNPM_ALLOC_PAGE_ORDER); + // page = dev_alloc_pages(rnpm_rx_pg_order(rx_ring)); + if (unlikely(!page)) { + rx_ring->rx_stats.alloc_rx_page_failed++; + return false; + } + + bi->page_offset = rnpm_rx_offset(rx_ring); + + /* map page for use */ + dma = dma_map_page_attrs(rx_ring->dev, page, bi->page_offset, bufsz, + DMA_FROM_DEVICE, RNPM_RX_DMA_ATTR); + + /* if mapping failed free memory back to system since + * there isn't much point in holding memory we can't use + */ + if (dma_mapping_error(rx_ring->dev, dma)) { + //__free_pages(page, rnpm_rx_pg_order(rx_ring)); + __free_pages(page, RNPM_ALLOC_PAGE_ORDER); + + rx_ring->rx_stats.alloc_rx_page_failed++; + return false; + } + bi->dma = dma; + bi->page = page; + bi->page_offset = rnpm_rx_offset(rx_ring); + page_ref_add(page, USHRT_MAX - 1); + bi->pagecnt_bias = USHRT_MAX; + rx_ring->rx_stats.alloc_rx_page++; + + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, 0, bufsz, + DMA_FROM_DEVICE); + + /* Refresh the desc even if buffer_addrs didn't change + * because each write-back erases this info. + */ + rx_desc->pkt_addr = cpu_to_le64(bi->dma + fun_id); + + return true; +} + +#else +static bool rnpm_alloc_mapped_page(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *bi) +{ + struct page *page = bi->page; + dma_addr_t dma; + + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) + return true; + + /* alloc new page for storage */ + page = dev_alloc_pages(rnpm_rx_pg_order(rx_ring)); + + if (unlikely(!page)) { + rx_ring->rx_stats.alloc_rx_page_failed++; + return false; + } + + /* map page for use */ + dma = dma_map_page_attrs(rx_ring->dev, page, 0, + rnpm_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + + /* if mapping failed free memory back to system since + * there isn't much point in holding memory we can't use + */ + if (dma_mapping_error(rx_ring->dev, dma)) { + __free_pages(page, rnpm_rx_pg_order(rx_ring)); + + rx_ring->rx_stats.alloc_rx_page_failed++; + return false; + } + /* used temp */ + // rx_ring->rx_stats.alloc_rx_page_failed++; + bi->dma = dma; + bi->page = page; + bi->page_offset = rnpm_rx_offset(rx_ring); +#ifdef HAVE_PAGE_COUNT_BULK_UPDATE + page_ref_add(page, USHRT_MAX - 1); + bi->pagecnt_bias = USHRT_MAX; +#else + bi->pagecnt_bias = 1; +#endif + rx_ring->rx_stats.alloc_rx_page++; + + return true; +} +#endif /* RNPM_OPTM_WITH_LPAGE */ +/** + * rnpm_pull_tail - rnpm specific version of skb_pull_tail + * @skb: pointer to current skb being adjusted + * + * This function is an rnpm specific version of __pskb_pull_tail. The + * main difference between this version and the original function is that + * this function can make several assumptions about the state of things + * that allow for significant optimizations versus the standard function. + * As a result we can do things like drop a frag and maintain an accurate + * truesize for the skb. + */ +static void rnpm_pull_tail(struct sk_buff *skb) +{ + skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; + unsigned char *va; + unsigned int pull_len; + + /* it is valid to use page_address instead of kmap since we are + * working with pages allocated out of the lomem pool per + * alloc_page(GFP_ATOMIC) + */ + va = skb_frag_address(frag); + + /* we need the header to contain the greater of either ETH_HLEN or + * 60 bytes if the skb->len is less than 60 for skb_pad. + */ + pull_len = rnpm_get_headlen(va, RNPM_RX_HDR_SIZE); + /* align pull length to size of long to optimize memcpy performance */ + skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long))); + /* update all of the pointers */ + skb_frag_size_sub(frag, pull_len); + skb_frag_off_add(frag, pull_len); + skb->data_len -= pull_len; + skb->tail += pull_len; +} + +/** + * rnpm_dma_sync_frag - perform DMA sync for first frag of SKB + * @rx_ring: rx descriptor ring packet is being transacted on + * @skb: pointer to current skb being updated + * + * This function provides a basic DMA sync up for the first fragment of an + * skb. The reason for doing this is that the first fragment cannot be + * unmapped until we have reached the end of packet descriptor for a buffer + * chain. + */ +__maybe_unused static void rnpm_dma_sync_frag(struct rnpm_ring *rx_ring, + struct sk_buff *skb) +{ + /* if the page was released unmap it, else just sync our portion */ + if (unlikely(RNPM_CB(skb)->page_released)) { + dma_unmap_page_attrs(rx_ring->dev, RNPM_CB(skb)->dma, + rnpm_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + } else if (ring_uses_build_skb(rx_ring)) { + unsigned long offset = (unsigned long)(skb->data) & ~PAGE_MASK; + + dma_sync_single_range_for_cpu(rx_ring->dev, RNPM_CB(skb)->dma, + offset, skb_headlen(skb), + DMA_FROM_DEVICE); + } else { + skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; + + dma_sync_single_range_for_cpu(rx_ring->dev, RNPM_CB(skb)->dma, + skb_frag_off(frag), + skb_frag_size(frag), + DMA_FROM_DEVICE); + } +} + +/** + * rnpm_cleanup_headers - Correct corrupted or empty headers + * @rx_ring: rx descriptor ring packet is being transacted on + * @rx_desc: pointer to the EOP Rx descriptor + * @skb: pointer to current skb being fixed + * + * Check if the skb is valid. In the XDP case it will be an error pointer. + * Return true in this case to abort processing and advance to next + * descriptor. + * + * Check for corrupted packet headers caused by senders on the local L2 + * embedded NIC switch not setting up their Tx Descriptors right. These + * should be very rare. + * + * Also address the case where we are pulling data in on pages only + * and as such no data is present in the skb header. + * + * In addition if skb is not at least 60 bytes we need to pad it so that + * it is large enough to qualify as a valid Ethernet frame. + * + * Returns true if an error was encountered and skb was freed. + **/ +static bool rnpm_cleanup_headers(struct rnpm_ring __maybe_unused *rx_ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff *skb) +{ + /* XDP packets use error pointer so abort at this point */ +#ifdef RNPM_OPTM_WITH_LPAGE +#else + if (IS_ERR(skb)) + return true; +#endif + /* place header in linear portion of buffer */ + if (!skb_headlen(skb)) + rnpm_pull_tail(skb); + + /* if eth_skb_pad returns an error the skb was freed */ + if (eth_skb_pad(skb)) + return true; + + return false; +} + +/** + * rnpm_reuse_rx_page - page flip buffer and store it back on the ring + * @rx_ring: rx descriptor ring to store buffers on + * @old_buff: donor buffer to have page reused + * + * Synchronizes page for reuse by the adapter + **/ +static void rnpm_reuse_rx_page(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *old_buff) +{ + struct rnpm_rx_buffer *new_buff; + u16 nta = rx_ring->next_to_alloc; + + new_buff = &rx_ring->rx_buffer_info[nta]; + + /* update, and store next to alloc */ + nta++; + rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; + + /* Transfer page from old buffer to new buffer. + * Move each member individually to avoid possible store + * forwarding stalls and unnecessary copy of skb. + */ + new_buff->dma = old_buff->dma; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; +} + +static inline bool rnpm_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); +} + +static bool rnpm_can_reuse_rx_page(struct rnpm_rx_buffer *rx_buffer) +{ + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; + +#ifdef RNPM_OPTM_WITH_LPAGE + return false; +#endif + /* avoid re-using remote pages */ + if (unlikely(rnpm_page_is_reserved(page))) + return false; +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ +#ifdef HAVE_PAGE_COUNT_BULK_UPDATE + if (unlikely((page_ref_count(page) - pagecnt_bias) > 1)) +#else + if (unlikely((page_count(page) - pagecnt_bias) > 1)) +#endif + return false; +#else + /* The last offset is a bit aggressive in that we assume the + * worst case of FCoE being enabled and using a 3K buffer. + * However this should have minimal impact as the 1K extra is + * still less than one buffer in size. + */ +#define RNPM_LAST_OFFSET (SKB_WITH_OVERHEAD(PAGE_SIZE) - RNPM_RXBUFFER_2K) + if (rx_buffer->page_offset > RNPM_LAST_OFFSET) + return false; +#endif + +#ifdef HAVE_PAGE_COUNT_BULK_UPDATE + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX - 1); + rx_buffer->pagecnt_bias = USHRT_MAX; + } +#else + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ + if (likely(!pagecnt_bias)) { + page_ref_inc(page); + rx_buffer->pagecnt_bias = 1; + } +#endif + + return true; +} + +/** + * rnpm_add_rx_frag - Add contents of Rx buffer to sk_buff + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: buffer containing page to add + * @skb: sk_buff to place the data into + * @size: size of data + * + * This function will add the data contained in rx_buffer->page to the skb. + * This is done either through a direct copy if the data in the buffer is + * less than the skb header size, otherwise it will just attach the page as + * a frag to the skb. + * + * The function will then update the page offset if necessary and return + * true if the buffer can be reused by the adapter. + **/ +static void rnpm_add_rx_frag(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + struct sk_buff *skb, unsigned int size) +{ +#if (PAGE_SIZE < 8192) + unsigned int truesize = rnpm_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = ring_uses_build_skb(rx_ring) ? + SKB_DATA_ALIGN(RNPM_SKB_PAD + size) : + SKB_DATA_ALIGN(size); +#endif + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); + +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + +#ifdef RNPM_OPTM_WITH_LPAGE +static struct rnpm_rx_buffer *rnpm_get_rx_buffer(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, + const unsigned int size) +{ + struct rnpm_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; + prefetchw(rx_buffer->page); + + rx_buf_dump("rx buf", + page_address(rx_buffer->page) + rx_buffer->page_offset, + rx_desc->wb.len); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, 0, size, + DMA_FROM_DEVICE); + /* skip_sync: */ + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} +#else +static struct rnpm_rx_buffer *rnpm_get_rx_buffer(struct rnpm_ring *rx_ring, + union rnpm_rx_desc *rx_desc, + struct sk_buff **skb, + const unsigned int size) +{ + struct rnpm_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; + prefetchw(rx_buffer->page); + *skb = rx_buffer->skb; + + rx_buf_dump("rx buf", + page_address(rx_buffer->page) + rx_buffer->page_offset, + rx_desc->wb.len); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, + rx_buffer->page_offset, size, + DMA_FROM_DEVICE); + // skip_sync: + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} +#endif + +#ifdef RNPM_OPTM_WITH_LPAGE +static void rnpm_put_rx_buffer(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer) +{ + if (rnpm_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + rnpm_reuse_rx_page(rx_ring, rx_buffer); + } else { + /* we are not reusing the buffer so unmap it */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + rnpm_rx_bufsz(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; + // rx_buffer->skb = NULL; +} + +#else +static void rnpm_put_rx_buffer(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + struct sk_buff *skb) +{ + if (!rx_buffer || !rx_buffer->page || !rx_ring) { + rnpm_info("rnpm rx buffer is null!\n"); + WARN_ON(1); + return; + } + + if (rnpm_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + rnpm_reuse_rx_page(rx_ring, rx_buffer); + } else { + /* no need to delay unmap */ + // if (!IS_ERR(skb) && RNPM_CB(skb)->dma == rx_buffer->dma) { + // /* the page has been released from the ring */ + // RNPM_CB(skb)->page_released = true; + // } else { + /* we are not reusing the buffer so unmap it */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + rnpm_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + // } + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; + rx_buffer->skb = NULL; +} +#endif +#ifdef RNPM_OPTM_WITH_LPAGE +static struct sk_buff *rnpm_construct_skb(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + union rnpm_rx_desc *rx_desc, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; + unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int headlen; + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + /* Note, we get here by enabling legacy-rx via: + * + * ethtool --set-priv-flags legacy-rx on + * + * In this mode, we currently get 0 extra XDP headroom as + * opposed to having legacy-rx off, where we process XDP + * packets going to stack via rnpm_build_skb(). The latter + * provides us currently with 192 bytes of headroom. + * + * For rnpm_construct_skb() mode it means that the + * xdp->data_meta will always point to xdp->data, since + * the helper cannot expand the head. Should this ever + * change in future for legacy-rx mode on, then lets also + * add xdp->data_meta handling here. + */ + + /* allocate a skb to store the frags */ + skb = napi_alloc_skb(&rx_ring->q_vector->napi, RNPM_RX_HDR_SIZE); + if (unlikely(!skb)) + return NULL; + + prefetchw(skb->data); + + /* Determine available headroom for copy */ + headlen = size; + if (headlen > RNPM_RX_HDR_SIZE) + headlen = rnpm_get_headlen(va, RNPM_RX_HDR_SIZE); + + /* align pull length to size of long to optimize memcpy performance */ + memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long))); + + /* update all of the pointers */ + size -= headlen; + + if (size) { + skb_add_rx_frag(skb, 0, rx_buffer->page, + (va + headlen) - page_address(rx_buffer->page), + size, truesize); + rx_buffer->page_offset += truesize; + } else { + rx_buffer->pagecnt_bias++; + } + + return skb; +} + +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC +static struct sk_buff *rnpm_build_skb(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + union rnpm_rx_desc *rx_desc, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; + unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(size + RNPM_SKB_PAD); + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + /* build an skb around the page buffer */ + skb = build_skb(va - RNPM_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, RNPM_SKB_PAD); + __skb_put(skb, size); + /* record DMA address if this is the start of a + * chain of buffers + */ + /* if (!rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_EOP)) + * RNPM_CB(skb)->dma = rx_buffer->dma; + */ + // check_udp_chksum((void *)skb->data, rx_buffer); + /* update buffer offset */ + // no need this , we not use this page again + // rx_buffer->page_offset += truesize; + + return skb; +} + +#endif /* HAVE_SWIOTLB_SKIP_CPU_SYNC */ + +#else +static struct sk_buff *rnpm_construct_skb(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + struct xdp_buff *xdp, + union rnpm_rx_desc *rx_desc) +{ + unsigned int size = xdp->data_end - xdp->data; +#if (PAGE_SIZE < 8192) + unsigned int truesize = rnpm_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = + SKB_DATA_ALIGN(xdp->data_end - xdp->data_hard_start); +#endif + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(xdp->data); +#if L1_CACHE_BYTES < 128 + prefetch(xdp->data + L1_CACHE_BYTES); +#endif + /* Note, we get here by enabling legacy-rx via: + * + * ethtool --set-priv-flags legacy-rx on + * + * In this mode, we currently get 0 extra XDP headroom as + * opposed to having legacy-rx off, where we process XDP + * packets going to stack via rnpm_build_skb(). The latter + * provides us currently with 192 bytes of headroom. + * + * For rnpm_construct_skb() mode it means that the + * xdp->data_meta will always point to xdp->data, since + * the helper cannot expand the head. Should this ever + * change in future for legacy-rx mode on, then lets also + * add xdp->data_meta handling here. + */ + + /* allocate a skb to store the frags */ + skb = napi_alloc_skb(&rx_ring->q_vector->napi, RNPM_RX_HDR_SIZE); + if (unlikely(!skb)) + return NULL; + + prefetchw(skb->data); + + if (size > RNPM_RX_HDR_SIZE) { + skb_add_rx_frag(skb, 0, rx_buffer->page, + xdp->data - page_address(rx_buffer->page), size, + truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + } else { + memcpy(__skb_put(skb, size), xdp->data, + ALIGN(size, sizeof(long))); + rx_buffer->pagecnt_bias++; + } + + return skb; +} + +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC +static struct sk_buff *rnpm_build_skb(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + struct xdp_buff *xdp, + union rnpm_rx_desc *rx_desc) +{ +#ifdef HAVE_XDP_BUFF_DATA_META + unsigned int metasize = xdp->data - xdp->data_meta; + void *va = xdp->data_meta; +#else + void *va = xdp->data; +#endif +#if (PAGE_SIZE < 8192) + unsigned int truesize = rnpm_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(xdp->data_end - xdp->data_hard_start); +#endif + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + /* build an skb around the page buffer */ + skb = build_skb(xdp->data_hard_start, truesize); + if (unlikely(!skb)) + return NULL; + /* update pointers within the skb to store the data */ + skb_reserve(skb, xdp->data - xdp->data_hard_start); + __skb_put(skb, xdp->data_end - xdp->data); +#ifdef HAVE_XDP_BUFF_DATA_META + if (metasize) + skb_metadata_set(skb, metasize); +#endif + + /* update buffer offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + +#endif /* HAVE_SWIOTLB_SKIP_CPU_SYNC */ +#endif + +#define RNPM_XDP_PASS 0 +#define RNPM_XDP_CONSUMED 1 +#define RNPM_XDP_TX 2 + +#ifndef RNPM_OPTM_WITH_LPAGE +static void rnpm_rx_buffer_flip(struct rnpm_ring *rx_ring, + struct rnpm_rx_buffer *rx_buffer, + unsigned int size) +{ +#if (PAGE_SIZE < 8192) + unsigned int truesize = rnpm_rx_pg_size(rx_ring) / 2; + + rx_buffer->page_offset ^= truesize; +#else + unsigned int truesize = ring_uses_build_skb(rx_ring) ? + SKB_DATA_ALIGN(RNPM_SKB_PAD + size) : + SKB_DATA_ALIGN(size); + + rx_buffer->page_offset += truesize; +#endif +} +#endif + +#ifdef RNPM_OPTM_WITH_LPAGE +static int rnpm_clean_rx_irq(struct rnpm_q_vector *q_vector, + struct rnpm_ring *rx_ring, int budget) +{ + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + unsigned int driver_drop_packets = 0; + struct sk_buff *skb = rx_ring->skb; + struct rnpm_adapter *adapter = q_vector->adapter; + u16 cleaned_count = rnpm_desc_unused_rx(rx_ring); + + // rx_ring->rx_stats.poll_count++; + while (likely(total_rx_packets < budget)) { + union rnpm_rx_desc *rx_desc; + struct rnpm_rx_buffer *rx_buffer; + // struct sk_buff *skb; + unsigned int size; + + /* return some buffers to hardware, one at a time is too slow */ + if (cleaned_count >= RNPM_RX_BUFFER_WRITE) { + rnpm_alloc_rx_buffers(rx_ring, cleaned_count); + cleaned_count = 0; + } + rx_desc = RNPM_RX_DESC(rx_ring, rx_ring->next_to_clean); + + rx_buf_dump("rx-desc:", rx_desc, sizeof(*rx_desc)); + // buf_dump("rx-desc:", rx_desc, sizeof(*rx_desc)); + rx_debug_printk(" dd set: %s\n", + (rx_desc->wb.cmd & RNPM_RXD_STAT_DD) ? "Yes" : + "No"); + + if (!rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_DD)) + break; + + /* This memory barrier is needed to keep us from reading + * any other fields out of the rx_desc until we know the + * descriptor has been written back + */ + dma_rmb(); + + rx_debug_printk( + "queue:%d rx-desc:%d has-data len:%d next_to_clean %d\n", + rx_ring->rnpm_queue_idx, rx_ring->next_to_clean, + rx_desc->wb.len, rx_ring->next_to_clean); + + /* handle padding */ + if ((adapter->priv_flags & + RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) && + (!(adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG))) { + if (likely(rnpm_test_staterr(rx_desc, + RNPM_RXD_STAT_EOP))) { + size = le16_to_cpu(rx_desc->wb.len) - + le16_to_cpu(rx_desc->wb.padding_len); + } else { + size = le16_to_cpu(rx_desc->wb.len); + } + } else { + /* size should not zero */ + size = le16_to_cpu(rx_desc->wb.len); + } + + if (!size) + break; + + if (rnpm_check_csum_error(rx_ring, rx_desc, size, + &driver_drop_packets)) { + cleaned_count++; + continue; + } + + rx_buffer = rnpm_get_rx_buffer(rx_ring, rx_desc, size); + + if (skb) { + rnpm_add_rx_frag(rx_ring, rx_buffer, skb, size); +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC + } else if (ring_uses_build_skb(rx_ring)) { + skb = rnpm_build_skb(rx_ring, rx_buffer, rx_desc, size); +#endif + } else { + skb = rnpm_construct_skb(rx_ring, rx_buffer, rx_desc, + size); + } + + /* exit if we failed to retrieve a buffer */ + if (!skb) { + rx_ring->rx_stats.alloc_rx_buff_failed++; + rx_buffer->pagecnt_bias++; + break; + } + +#ifndef NO_PTP + if (module_enable_ptp && adapter->ptp_rx_en && + adapter->flags2 & RNPM_FLAG2_PTP_ENABLED) { + rnpm_ptp_get_rx_hwstamp(adapter, rx_desc, skb); + } +#endif + rnpm_put_rx_buffer(rx_ring, rx_buffer); + cleaned_count++; + + /* place incomplete frames back on ring for completion */ + if (rnpm_is_non_eop(rx_ring, rx_desc)) { + // skb = NULL; + continue; + } + + /* verify the packet layout is correct */ + if (rnpm_cleanup_headers(rx_ring, rx_desc, skb)) { + skb = NULL; + continue; + } + + /* probably a little skewed due to removing CRC */ + total_rx_bytes += skb->len; + + /* populate checksum, timestamp, VLAN, and protocol */ + rnpm_process_skb_fields(rx_ring, rx_desc, skb); + + rnpm_rx_skb(q_vector, skb); + skb = NULL; + total_rx_packets++; + + /* update budget accounting */ + } + rx_ring->skb = skb; + + u64_stats_update_begin(&rx_ring->syncp); + rx_ring->stats.packets += total_rx_packets; + rx_ring->stats.bytes += total_rx_bytes; + rx_ring->rx_stats.driver_drop_packets += driver_drop_packets; + u64_stats_update_end(&rx_ring->syncp); + q_vector->rx.total_packets += total_rx_packets; + q_vector->rx.total_bytes += total_rx_bytes; + if (total_rx_packets) + q_vector->rx.poll_times++; + + if (total_rx_packets >= budget) + rx_ring->rx_stats.poll_again_count++; + return total_rx_packets; +} +#else /* RNPM_OPTM_WITH_LPAGE */ +/** + * rnpm_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf + * @q_vector: structure containing interrupt and ring information + * @rx_ring: rx descriptor ring to transact packets on + * @budget: Total limit on number of packets to process + * + * This function provides a "bounce buffer" approach to Rx interrupt + * processing. The advantage to this is that on systems that have + * expensive overhead for IOMMU access this provides a means of avoiding + * it by maintaining the mapping of the page to the system. + * + * Returns amount of work completed. + **/ + +static int rnpm_clean_rx_irq(struct rnpm_q_vector *q_vector, + struct rnpm_ring *rx_ring, int budget) +{ + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + unsigned int driver_drop_packets = 0; + struct rnpm_adapter *adapter = q_vector->adapter; + u16 cleaned_count = rnpm_desc_unused_rx(rx_ring); + bool xdp_xmit = false; + struct xdp_buff xdp; + + xdp.data = NULL; + xdp.data_end = NULL; + + // rx_ring->rx_stats.poll_count++; + while (likely(total_rx_packets < budget)) { + union rnpm_rx_desc *rx_desc; + struct rnpm_rx_buffer *rx_buffer; + struct sk_buff *skb; + unsigned int size; + + /* return some buffers to hardware, one at a time is too slow */ + if (cleaned_count >= RNPM_RX_BUFFER_WRITE) { + rnpm_alloc_rx_buffers(rx_ring, cleaned_count); + cleaned_count = 0; + } + rx_desc = RNPM_RX_DESC(rx_ring, rx_ring->next_to_clean); + + rx_buf_dump("rx-desc:", rx_desc, sizeof(*rx_desc)); + // buf_dump("rx-desc:", rx_desc, sizeof(*rx_desc)); + rx_debug_printk(" dd set: %s\n", + (rx_desc->wb.cmd & RNPM_RXD_STAT_DD) ? "Yes" : + "No"); + + if (!rnpm_test_staterr(rx_desc, RNPM_RXD_STAT_DD)) + break; + + /* This memory barrier is needed to keep us from reading + * any other fields out of the rx_desc until we know the + * descriptor has been written back + */ + dma_rmb(); + + rx_debug_printk( + "queue:%d rx-desc:%d has-data len:%d next_to_clean %d\n", + rx_ring->rnpm_queue_idx, rx_ring->next_to_clean, + rx_desc->wb.len, rx_ring->next_to_clean); + + /* handle padding */ + if ((adapter->priv_flags & + RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) && + (!(adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG))) { + if (likely(rnpm_test_staterr(rx_desc, + RNPM_RXD_STAT_EOP))) { + size = le16_to_cpu(rx_desc->wb.len) - + le16_to_cpu(rx_desc->wb.padding_len); + } else { + size = le16_to_cpu(rx_desc->wb.len); + } + } else { + /* size should not zero */ + size = le16_to_cpu(rx_desc->wb.len); + } + + if (!size) + break; + + if (rnpm_check_csum_error(rx_ring, rx_desc, size, + &driver_drop_packets)) { + cleaned_count++; + continue; + } + + rx_buffer = rnpm_get_rx_buffer(rx_ring, rx_desc, &skb, size); + + if (!skb) { + xdp.data = page_address(rx_buffer->page) + + rx_buffer->page_offset; +#ifdef HAVE_XDP_BUFF_DATA_META + xdp.data_meta = xdp.data; +#endif + xdp.data_hard_start = + xdp.data - rnpm_rx_offset(rx_ring); + xdp.data_end = xdp.data + size; + /* call xdp hook use this to support xdp hook */ + // skb = rnpm_run_xdp(adapter, rx_ring, &xdp); + } + + if (IS_ERR(skb)) { + if (PTR_ERR(skb) == -RNPM_XDP_TX) { + xdp_xmit = true; + rnpm_rx_buffer_flip(rx_ring, rx_buffer, size); + } else { + rx_buffer->pagecnt_bias++; + } + total_rx_packets++; + total_rx_bytes += size; + } else if (skb) { + rnpm_add_rx_frag(rx_ring, rx_buffer, skb, size); +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC + } else if (ring_uses_build_skb(rx_ring)) { + skb = rnpm_build_skb(rx_ring, rx_buffer, &xdp, rx_desc); +#endif + } else { + skb = rnpm_construct_skb(rx_ring, rx_buffer, &xdp, + rx_desc); + } + + /* exit if we failed to retrieve a buffer */ + if (!skb) { + rx_ring->rx_stats.alloc_rx_buff_failed++; + rx_buffer->pagecnt_bias++; + break; + } + +#ifndef NO_PTP + if (module_enable_ptp && adapter->ptp_rx_en && + adapter->flags2 & RNPM_FLAG2_PTP_ENABLED) { + rnpm_ptp_get_rx_hwstamp(adapter, rx_desc, skb); + } +#endif + rnpm_put_rx_buffer(rx_ring, rx_buffer, skb); + cleaned_count++; + + /* place incomplete frames back on ring for completion */ + if (rnpm_is_non_eop(rx_ring, rx_desc, skb)) + continue; + + /* verify the packet layout is correct */ + if (rnpm_cleanup_headers(rx_ring, rx_desc, skb)) + continue; + + /* probably a little skewed due to removing CRC */ + total_rx_bytes += skb->len; + total_rx_packets++; + + /* populate checksum, timestamp, VLAN, and protocol */ + rnpm_process_skb_fields(rx_ring, rx_desc, skb); + + rnpm_rx_skb(q_vector, skb); + + /* update budget accounting */ + } + + u64_stats_update_begin(&rx_ring->syncp); + rx_ring->stats.packets += total_rx_packets; + rx_ring->stats.bytes += total_rx_bytes; + rx_ring->rx_stats.driver_drop_packets += driver_drop_packets; + u64_stats_update_end(&rx_ring->syncp); + q_vector->rx.total_packets += total_rx_packets; + q_vector->rx.total_bytes += total_rx_bytes; + if (total_rx_packets) + q_vector->rx.poll_times++; + + if (total_rx_packets >= budget) + rx_ring->rx_stats.poll_again_count++; + return total_rx_packets; +} +#endif /* RNPM_OPTM_WITH_LPAGE */ + + +/** + * rnpm_configure_msix - Configure MSI-X hardware + * @adapter: board private structure + * + * rnpm_configure_msix sets up the hardware to properly generate MSI-X + * interrupts. + **/ +static void rnpm_configure_msix(struct rnpm_adapter *adapter) +{ + struct rnpm_q_vector *q_vector; + int i; + // u32 mask; + + // rnpm_dbg("[%s] num_q_vectors:%d\n", __func__, adapter->num_q_vectors); + + /* configure ring-msix Registers table */ + for (i = 0; i < adapter->num_q_vectors; i++) { + struct rnpm_ring *ring; + + q_vector = adapter->q_vector[i]; + rnpm_for_each_ring(ring, q_vector->rx) rnpm_set_ring_vector( + adapter, ring->rnpm_queue_idx, q_vector->v_idx); + } +} + +static inline bool rnpm_container_is_rx(struct rnpm_q_vector *q_vector, + struct rnpm_ring_container *rc) +{ + return &q_vector->rx == rc; +} + +/** + * ixgbe_write_eitr - write EITR register in hardware specific way + * @q_vector: structure containing interrupt and ring information + * + * This function is made to be called by ethtool and by the driver + * when it needs to update EITR registers at runtime. Hardware + * specific quirks/differences are taken care of here. + */ +void rnpm_write_eitr(struct rnpm_q_vector *q_vector, bool is_rxframe) +{ + struct rnpm_adapter *adapter = q_vector->adapter; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *ring; + u32 itr_reg = q_vector->adapter->rx_usecs * hw->usecstocount; + + if (is_rxframe) { + rnpm_for_each_ring(ring, q_vector->rx) wr32( + hw, + RNPM_DMA_REG_RX_INT_DELAY_PKTCNT(ring->rnpm_queue_idx), + q_vector->itr); + } else { + rnpm_for_each_ring(ring, q_vector->rx) wr32( + hw, + RNPM_DMA_REG_RX_INT_DELAY_TIMER(ring->rnpm_queue_idx), + itr_reg); + } +} + +static int rnpm_update_itr_by_packets(int speed, int poll_packets, int itr) +{ + unsigned int t; + + if (speed >= SPEED_10000) { + /* 10G */ + if (((poll_packets - itr) == 1)) { + /* Hold this itr */ + } else { + if (poll_packets == itr) { + } else if (poll_packets > itr) { + t = DIV_ROUND_UP(poll_packets - itr, 2); + if (t > 2) + t = 2; + itr += t ? t : 1; + } else { + itr >>= 1; + } + } + if (itr < 3) + itr = 3; + } else if (speed >= SPEED_1000) { + /* 1G */ + if (((poll_packets - itr) == 1) || + ((poll_packets - itr) == 2)) { + /* Hold this itr */ + } else { + if (poll_packets >= itr) { + t = DIV_ROUND_UP(poll_packets - itr, 2); + if (t > 2) + t = 2; + itr += t ? t : 1; + } else { + if (itr >= (poll_packets + 2)) { + t = DIV_ROUND_UP(itr - poll_packets, 2); + itr -= t ? 2 : 1; + } else + itr--; + } + } + if (itr < 3) + itr = 3; + } else { + /* 100M/10M */ + if (((poll_packets - itr) == 1) && (itr != 1)) { + /* Hold this itr */ + } else { + if (poll_packets >= itr) { + t = DIV_ROUND_UP(poll_packets - itr, 2); + if (t > 2) + t = 2; + itr += t ? t : 1; + } else { + itr--; + } + } + if (itr < 3) + itr = 3; + } + + return itr; +} + +static bool rnpm_update_rxf(struct rnpm_q_vector *q_vector, + struct rnpm_ring_container *ring_container) +{ + int itr = 1; + unsigned int avg_wire_size, packets, bytes, t; + int poll_packets = 0; + unsigned long next_update = jiffies; + int factor, off_1, off_2, speed; + bool ret = true; + + /* If we don't have any rings just leave ourselves set for maximum + * possible latency so we take ourselves out of the equation. + */ + + if (!ring_container->ring) + return false; + + factor = q_vector->factor; + packets = ring_container->total_packets / factor; + bytes = ring_container->total_bytes / factor; + + /* Rx packets is zero, no need modify itr */ + if (!packets) + return false; + + switch (q_vector->adapter->link_speed) { + case RNPM_LINK_SPEED_10GB_FULL: + off_1 = 24; + off_2 = 10; + speed = SPEED_10000; + break; + // case RNPM_LINK_SPEED_2_5GB_FULL: + case RNPM_LINK_SPEED_1GB_FULL: + off_1 = 0; + off_2 = 0; + speed = SPEED_1000; + break; + case RNPM_LINK_SPEED_100_FULL: + case RNPM_LINK_SPEED_10_FULL: + off_1 = 0; + off_2 = -12; + speed = SPEED_100; + break; + default: + off_1 = 24; + off_2 = 10; + speed = SPEED_10000; + break; + } + + /* If we didn't update within up to 1 - 2 jiffies we can assume + * that either packets are coming in so slow there hasn't been + * any work, or that there is so much work that NAPI is dealing + * with interrupt moderation and we don't need to do anything. + */ + if (time_after_eq(next_update, ring_container->next_update)) { + avg_wire_size = bytes / packets; + if (rnpm_container_is_rx(q_vector, ring_container) && + (speed > SPEED_100)) { + /* If Rx and there are 1 to 23 packets and bytes are less than + * 12112 assume insufficient data to use bulk rate limiting + * approach. Instead we will focus on simply trying to target + * receiving 8 times as much data in the next interrupt. Assume + * max packert is 1514 bytes(1514*8 = 12112), min len is 66 bytes + */ + if (packets && packets < (24 + off_1) && + bytes < 12112 * DIV_ROUND_UP(factor, 2)) { + if ((packets <= 3) && (avg_wire_size <= 1120) && + (avg_wire_size >= 768)) + itr = 2; + else + itr = 1; + goto clear_counts; + } + } else { + if (packets && packets <= 3 && bytes < 6056) { + itr = 1; + goto clear_counts; + } + } + + itr = q_vector->itr; + + if (ring_container->poll_times && factor) { + t = (ring_container->poll_times > factor) ? + ring_container->poll_times / factor : + 1; + poll_packets = DIV_ROUND_UP(packets, t); + } else { + goto clear_counts; + } + + if (poll_packets <= (32 + off_2)) { + if ((poll_packets <= 3) && (avg_wire_size <= 1120) && + (speed > SPEED_100)) { + /* 1K - 2K bytes*/ + itr = 2; + } else { + itr = rnpm_update_itr_by_packets( + speed, poll_packets, itr); + } + } else { + /* Mabey too large */ + itr = q_vector->itr << 1; + if (itr > 64) + itr = 64; + } + ret = true; + } else { + ret = false; + goto out; + } + +clear_counts: + /* write back value */ + ring_container->itr = itr; + /* next update should occur within next jiffy */ + ring_container->next_update = next_update + 1; + ring_container->total_bytes = 0; + ring_container->total_packets = 0; + ring_container->poll_times = 0; + ring_container->ring->rx_stats.rx_poll_packets = packets; + ring_container->ring->rx_stats.rx_poll_avg_packets = poll_packets; + ring_container->ring->rx_stats.rx_poll_itr = itr; + +out: + return ret; +} + +/** + * rnpm_update_itr - update the dynamic ITR value based on statistics + * @q_vector: structure containing interrupt and ring information + * @ring_container: structure containing ring performance data + * + * Stores a new ITR value based on packets and byte + * counts during the last interrupt. The advantage of per interrupt + * computation is faster updates and more accurate ITR for the current + * traffic pattern. Constants in this function were computed + * based on theoretical maximum wire speed and thresholds were set based + * on testing data as well as attempting to minimize response time + * while increasing bulk throughput. + **/ +static bool __maybe_unused +rnpm_update_itr(struct rnpm_q_vector *q_vector, + struct rnpm_ring_container *ring_container) +{ + // unsigned int itr = RNPM_ITR_ADAPTIVE_MIN_USECS | + // RNPM_ITR_ADAPTIVE_LATENCY; + unsigned int itr = RNPM_ITR_ADAPTIVE_MIN_USECS; + unsigned int avg_wire_size, packets, bytes; + unsigned long next_update = jiffies; + + /* If we don't have any rings just leave ourselves set for maximum + * possible latency so we take ourselves out of the equation. + */ + + if (!ring_container->ring) + + packets = ring_container->total_packets; + bytes = ring_container->total_bytes; + + /* Rx packets is zero, no need modify itr */ + if (!packets) + return false; + + packets = ring_container->total_packets; + bytes = ring_container->total_bytes; + + /* Rx packets is zero, no need modify itr */ + if (!packets) + return false; + + /* If we didn't update within up to 1 - 2 jiffies we can assume + * that either packets are coming in so slow there hasn't been + * any work, or that there is so much work that NAPI is dealing + * with interrupt moderation and we don't need to do anything. + */ + if (time_after(next_update, ring_container->next_update)) { + itr = q_vector->itr; + goto clear_counts; + } + + if (rnpm_container_is_rx(q_vector, ring_container)) { + /* If Rx and there are 1 to 23 packets and bytes are less than + * 12112 assume insufficient data to use bulk rate limiting + * approach. Instead we will focus on simply trying to target + * receiving 8 times as much data in the next interrupt. + */ + + /* Assume max packert is 1514 bytes(1514*8 = 12112), head len is 66 + * bytes + */ + if (packets && packets < 24 && bytes < 12112) { + itr = RNPM_ITR_ADAPTIVE_MIN_USECS; + avg_wire_size = bytes + packets * 24; + avg_wire_size = clamp_t(unsigned int, avg_wire_size, + 128, 12800); + goto adjust_for_speed; + } + } + + /* Less than 48 packets we can assume that our current interrupt delay + * is only slightly too low. As such we should increase it by a small + * fixed amount. + */ + if (packets < 48) { + /* If sample size is 0 - 7 we should probably switch + * to latency mode instead of trying to control + * things as though we are in bulk. + * + * Otherwise if the number of packets is less than 48 + * we should maintain whatever mode we are currently + * in. The range between 8 and 48 is the cross-over + * point between latency and bulk traffic. + */ + if (packets && packets < 8) { + itr += RNPM_ITR_ADAPTIVE_LATENCY; + } else { + itr = q_vector->itr + RNPM_ITR_ADAPTIVE_MIN_INC * 3; + if (itr > RNPM_ITR_ADAPTIVE_MAX_USECS) + itr = RNPM_ITR_ADAPTIVE_MAX_USECS; + } + goto clear_counts; + } + + if (packets < 96) { + itr = q_vector->itr; + goto clear_counts; + } + + /* If packet count is 96 or greater we are likely looking at a slight + * overrun of the delay we want. Try halving our delay to see if that + * will cut the number of packets in half per interrupt. + */ + if (packets < 256) { + itr = q_vector->itr >> 2; + if (itr < RNPM_ITR_ADAPTIVE_MIN_USECS) + itr = RNPM_ITR_ADAPTIVE_MIN_USECS; + goto clear_counts; + } + + itr = RNPM_ITR_ADAPTIVE_BULK; + + // adjust_by_size: + /* If packet counts are 256 or greater we can assume we have a gross + * overestimation of what the rate should be. Instead of trying to fine + * tune it just use the formula below to try and dial in an exact value + * give the current packet size of the frame. + */ + avg_wire_size = bytes / packets; + + /* The following is a crude approximation of: + * wmem_default / (size + overhead) = desired_pkts_per_int + * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate + * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value + * + * Assuming wmem_default is 212992 and overhead is 640 bytes per + * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the + * formula down to + * + * (170 * (size + 24)) / (size + 640) = ITR + * + * We first do some math on the packet size and then finally bitshift + * by 8 after rounding up. We also have to account for PCIe link speed + * difference as ITR scales based on this. + */ + if (avg_wire_size <= 60) { + /* Start at 50k ints/sec */ + avg_wire_size = 5120; + } else if (avg_wire_size <= 316) { + /* 50K ints/sec to 16K ints/sec */ + avg_wire_size *= 40; + avg_wire_size += 2720; + } else if (avg_wire_size <= 1084) { + /* 16K ints/sec to 9.2K ints/sec */ + avg_wire_size *= 15; + avg_wire_size += 11452; + } else if (avg_wire_size <= 1980) { + /* 9.2K ints/sec to 8K ints/sec */ + avg_wire_size *= 5; + avg_wire_size += 22420; + } else { + /* plateau at a limit of 8K ints/sec */ + avg_wire_size = 32256; + } + +adjust_for_speed: + /* Resultant value is 256 times larger than it needs to be. This + * gives us room to adjust the value as needed to either increase + * or decrease the value based on link speeds of 10G, 2.5G, 1G, etc. + * + * Use addition as we have already recorded the new latency flag + * for the ITR value. + */ + switch (q_vector->adapter->link_speed) { + case RNPM_LINK_SPEED_10GB_FULL: + case RNPM_LINK_SPEED_100_FULL: + default: + itr += DIV_ROUND_UP(avg_wire_size, + RNPM_ITR_ADAPTIVE_MIN_INC * 256) * + RNPM_ITR_ADAPTIVE_MIN_INC; + break; + // case RNPM_LINK_SPEED_2_5GB_FULL: + case RNPM_LINK_SPEED_1GB_FULL: + // case RNPM_LINK_SPEED_10_FULL: + itr += DIV_ROUND_UP(avg_wire_size, + RNPM_ITR_ADAPTIVE_MIN_INC * 64) * + RNPM_ITR_ADAPTIVE_MIN_INC; + break; + } + // if ((itr & RNPM_ITR_ADAPTIVE_LATENCY) && itr < ring_container->itr) + // itr = ring_container->itr - RNPM_ITR_ADAPTIVE_MIN_INC; + +clear_counts: + /* write back value */ + if (ring_container->itr >= (itr + 12)) { + ring_container->itr = + (ring_container->itr >> 1) + RNPM_ITR_ADAPTIVE_MIN_INC; + } else { + ring_container->itr = itr; + } + + /* next update should occur within next jiffy */ + ring_container->next_update = next_update + 1; + + ring_container->total_bytes = 0; + ring_container->total_packets = 0; + return true; +} + +static void rnpm_set_itr(struct rnpm_q_vector *q_vector) +{ + u32 new_itr; + + if (rnpm_update_rxf(q_vector, &q_vector->rx)) { + new_itr = q_vector->rx.itr; + + if (new_itr != q_vector->itr) { + /* save the algorithm value here */ + q_vector->itr = new_itr; + rnpm_write_eitr(q_vector, 1); + } + } +} + +enum latency_range { + lowest_latency = 0, + low_latency = 1, + bulk_latency = 2, + latency_invalid = 255 +}; +__maybe_unused static void rnpm_check_sfp_event(struct rnpm_adapter *adapter, + u32 eicr) +{ + // struct rnpm_hw *hw = &adapter->hw; +} + +static inline void rnpm_irq_enable_queues(struct rnpm_adapter *adapter, + struct rnpm_q_vector *q_vector) +{ + struct rnpm_ring *ring; + // struct rnpm_hw *hw = &adapter->hw; + + rnpm_for_each_ring(ring, q_vector->rx) { + // clear irq + // rnpm_wr_reg(ring->dma_int_clr, RX_INT_MASK | TX_INT_MASK); + // wmb(); +#ifdef CONFIG_RNPM_DISABLE_TX_IRQ + rnpm_wr_reg(ring->dma_int_mask, ~(RX_INT_MASK)); +#else + rnpm_wr_reg(ring->dma_int_mask, ~(RX_INT_MASK | TX_INT_MASK)); + // rnpm_wr_reg(ring->dma_int_mask, ~(RX_INT_MASK)); +#endif + } +} + +static inline void rnpm_irq_disable_queues(struct rnpm_q_vector *q_vector) +{ + struct rnpm_ring *ring; + + rnpm_for_each_ring(ring, q_vector->tx) { + rnpm_wr_reg(ring->dma_int_mask, (RX_INT_MASK | TX_INT_MASK)); + // rnpm_wr_reg(ring->dma_int_clr, RX_INT_MASK | TX_INT_MASK); + } +} +/** + * rnpm_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ +static inline void rnpm_irq_enable(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_q_vectors; i++) + rnpm_irq_enable_queues(adapter, adapter->q_vector[i]); +} + +static irqreturn_t rnpm_msix_other(int irq, void *data) +{ + struct rnpm_pf_adapter *pf_adapter = data; + + rnpm_msg_task(pf_adapter); + + return IRQ_HANDLED; +} + +static void rnpm_htimer_start(struct rnpm_q_vector *q_vector) +{ + unsigned long ns = q_vector->irq_check_usecs * NSEC_PER_USEC / 2; + + hrtimer_start_range_ns(&q_vector->irq_miss_check_timer, ns_to_ktime(ns), + ns, HRTIMER_MODE_REL_PINNED); +} + +static void rnpm_htimer_stop(struct rnpm_q_vector *q_vector) +{ + hrtimer_cancel(&q_vector->irq_miss_check_timer); +} + +static irqreturn_t rnpm_msix_clean_rings(int irq, void *data) +{ + struct rnpm_q_vector *q_vector = data; + + rnpm_htimer_stop(q_vector); + /* disabled interrupts (on this vector) for us */ + rnpm_irq_disable_queues(q_vector); + + if (q_vector->rx.ring || q_vector->tx.ring) + napi_schedule_irqoff(&q_vector->napi); + + return IRQ_HANDLED; +} + +/** + * rnpm_poll - NAPI Rx polling callback + * @napi: structure for representing this polling device + * @budget: how many packets driver is allowed to clean + * + * This function is used for legacy and MSI, NAPI mode + **/ +int rnpm_poll(struct napi_struct *napi, int budget) +{ + struct rnpm_q_vector *q_vector = + container_of(napi, struct rnpm_q_vector, napi); + struct rnpm_adapter *adapter = q_vector->adapter; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *ring; + int per_ring_budget, work_done = 0; + bool clean_complete = true; + + /* Port is down/reset, but napi_schedule_irqoff is exec by watchdog task or + * irq_miss_check + */ + if (test_bit(__RNPM_RESETTING, &adapter->state) || + test_bit(__RNPM_DOWN, &adapter->state)) + return budget; + + rnpm_for_each_ring(ring, q_vector->tx) clean_complete &= + !!rnpm_clean_tx_irq(q_vector, ring, budget); + + if (budget <= 0) + return budget; + + /* attempt to distribute budget to each queue fairly, but don't allow + * the budget to go below 1 because we'll exit polling + */ + if (q_vector->rx.count > 1) + per_ring_budget = max(budget / q_vector->rx.count, 1); + else + per_ring_budget = budget; + rnpm_for_each_ring(ring, q_vector->rx) { + int cleaned = 0; + + /* this ring is waitting to reset rx_len*/ + /* avoid to deal this ring until reset done */ + if (likely(!(ring->ring_flags & RNPM_RING_FLAG_DO_RESET_RX_LEN))) + cleaned = rnpm_clean_rx_irq(q_vector, ring, + per_ring_budget); + /* check delay rx setup */ + if (unlikely(ring->ring_flags & + RNPM_RING_FLAG_DELAY_SETUP_RX_LEN)) { + int head; + + // maybe first stop ? + rnpm_disable_rx_queue(adapter, ring); + head = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD( + ring->rnpm_queue_idx)); + if (head < RNPM_MIN_RXD) { + /* it is time to delay set */ + /* stop rx */ + // rnpm_disable_rx_queue(adapter, ring); + ring->ring_flags &= + (~RNPM_RING_FLAG_DELAY_SETUP_RX_LEN); + ring->ring_flags |= + RNPM_RING_FLAG_DO_RESET_RX_LEN; + } else { + // start rx again + wr32(hw, + RNPM_DMA_RX_START(ring->rnpm_queue_idx), + 1); + } + } + work_done += cleaned; + if (cleaned >= per_ring_budget) + clean_complete = false; + } + + /* If all work not completed, return budget and keep polling */ + if (!clean_complete) { + int cpu_id = smp_processor_id(); + + /* It is possible that the interrupt affinity has changed but, + * if the cpu is pegged at 100%, polling will never exit while + * traffic continues and the interrupt will be stuck on this + * cpu. We check to make sure affinity is correct before we + * continue to poll, otherwise we must stop polling so the + * interrupt can move to the correct cpu. + */ + if (!cpumask_test_cpu(cpu_id, &q_vector->affinity_mask)) { + /* Tell napi that we are done polling */ + napi_complete_done(napi, work_done); + // printk("irq affinity\n"); + if (!test_bit(__RNPM_DOWN, &adapter->state)) + rnpm_irq_enable_queues(adapter, q_vector); + /* we need this to ensure riq start before tx start */ +#ifdef TX_IRQ_MISS_REDUCE + /* memory barrior */ + smp_mb(); + rnpm_for_each_ring(ring, q_vector->tx) + rnpm_check_restart_tx(q_vector, ring); +#endif + + if (!test_bit(__RNPM_DOWN, &adapter->state)) { + rnpm_htimer_start(q_vector); + /* Return budget-1 so that polling stops */ + return budget - 1; + } + } + +#ifdef TX_IRQ_MISS_REDUCE + rnpm_for_each_ring(ring, q_vector->tx) + rnpm_check_restart_tx(q_vector, ring); +#endif + /* do poll only state not down */ + if (!test_bit(__RNPM_DOWN, &adapter->state)) + return budget; + } + + if (likely(napi_complete_done(napi, work_done))) { + rnpm_set_itr(q_vector); + /* only open irq if not down */ + if (!test_bit(__RNPM_DOWN, &adapter->state)) + rnpm_irq_enable_queues(adapter, q_vector); + /* we need this to ensure irq start before tx start */ +#ifdef TX_IRQ_MISS_REDUCE + /* memory barrior */ + smp_mb(); + rnpm_for_each_ring(ring, q_vector->tx) { + rnpm_check_restart_tx(q_vector, ring); + } +#endif + } + /* only open htimer if net not down */ + if (!test_bit(__RNPM_DOWN, &adapter->state)) + rnpm_htimer_start(q_vector); + + return min(work_done, budget - 1); +} + +/** + * rnp_irq_affinity_notify - Callback for affinity changes + * @notify: context as to what irq was changed + * @mask: the new affinity mask + * + * This is a callback function used by the irq_set_affinity_notifier function + * so that we may register to receive changes to the irq affinity masks. + **/ +static void rnpm_irq_affinity_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct rnpm_q_vector *q_vector = + container_of(notify, struct rnpm_q_vector, affinity_notify); + + cpumask_copy(&q_vector->affinity_mask, mask); +} + +/** + * rnp_irq_affinity_release - Callback for affinity notifier release + * @ref: internal core kernel usage + * + * This is a callback function used by the irq_set_affinity_notifier function + * to inform the current notification subscriber that they will no longer + * receive notifications. + **/ +static void rnpm_irq_affinity_release(struct kref *ref) +{ +} + +/** + * rnpm_request_msix_irqs - Initialize MSI-X interrupts + * @adapter: board private structure + * + * rnpm_request_msix_irqs allocates MSI-X vectors and requests + * interrupts from the kernel. + **/ +static int rnpm_request_msix_irqs(struct rnpm_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err; + int i = 0; + int cpu; + + DPRINTK(IFUP, INFO, "num_q_vectors:%d\n", adapter->num_q_vectors); + + for (i = 0; i < adapter->num_q_vectors; i++) { + struct rnpm_q_vector *q_vector = adapter->q_vector[i]; + struct msix_entry *entry = &adapter->msix_entries[i]; + + // rnpm_dbg("use irq %d\n", entry->entry); + if (q_vector->tx.ring && q_vector->rx.ring) { + snprintf(q_vector->name, sizeof(q_vector->name) - 1, + "%s-%s-%d-%d", netdev->name, "TxRx", i, + q_vector->v_idx); + } else { + WARN(!(q_vector->tx.ring && q_vector->rx.ring), + "%s vector%d tx rx is null, v_idx:%d\n", + netdev->name, i, q_vector->v_idx); + /* skip this unused q_vector */ + continue; + } + err = request_irq(entry->vector, &rnpm_msix_clean_rings, 0, + q_vector->name, q_vector); + if (err) { + e_err(probe, + "%s:request_irq failed for MSIX interrupt:%d Error: %d\n", + netdev->name, entry->vector, err); + goto free_queue_irqs; + } + /* register for affinity change notifications */ + q_vector->affinity_notify.notify = rnpm_irq_affinity_notify; + q_vector->affinity_notify.release = rnpm_irq_affinity_release; + irq_set_affinity_notifier(entry->vector, + &q_vector->affinity_notify); + /* Spread affinity hints out across online CPUs. + * + * get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to pass to + * irq_set_affinity_hint without making a copy. + */ + cpu = cpumask_local_spread(q_vector->v_idx, -1); + irq_set_affinity_hint(entry->vector, get_cpu_mask(cpu)); + } + + return 0; + +free_queue_irqs: + while (i) { + i--; + irq_set_affinity_hint(adapter->msix_entries[i].vector, NULL); + irq_set_affinity_notifier(adapter->msix_entries[i].vector, + NULL); + irq_set_affinity_hint(adapter->msix_entries[i].vector, NULL); + free_irq(adapter->msix_entries[i].vector, adapter->q_vector[i]); + } + + kfree(adapter->msix_entries); + adapter->msix_entries = NULL; + return err; +} + +/** + * rnpm_request_irq - initialize interrupts + * @adapter: board private structure + * + * Attempts to configure interrupts using the best available + * capabilities of the hardware and kernel. + **/ +static int rnpm_request_irq(struct rnpm_adapter *adapter) +{ + int err; + + err = rnpm_request_msix_irqs(adapter); + if (err) + e_err(probe, "request_irq failed, Error %d\n", err); + + return err; +} + +static void rnpm_free_irq(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_q_vectors; i++) { + struct rnpm_q_vector *q_vector = adapter->q_vector[i]; + struct msix_entry *entry = &adapter->msix_entries[i]; + + /* free only the irqs that were actually requested */ + if (!q_vector->rx.ring && !q_vector->tx.ring) + continue; + /* clear the affinity notifier in the IRQ descriptor */ + irq_set_affinity_notifier(adapter->msix_entries[i].vector, + NULL); + /* clear the affinity_mask in the IRQ descriptor */ + irq_set_affinity_hint(entry->vector, NULL); + DPRINTK(IFDOWN, INFO, "free irq %s\n", q_vector->name); + free_irq(entry->vector, q_vector); + } +} + +/** + * rnpm_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ +static inline void rnpm_irq_disable(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_q_vectors; i++) { + rnpm_irq_disable_queues(adapter->q_vector[i]); + synchronize_irq(adapter->msix_entries[i].vector); + } +} + +int rnpm_xmit_nop_frame_ring(struct rnpm_adapter *adapter, + struct rnpm_ring *tx_ring) +{ + u16 i = tx_ring->next_to_use; + struct rnpm_tx_desc *tx_desc; + + tx_desc = RNPM_TX_DESC(tx_ring, i); + + /* set length to 0 */ + tx_desc->blen_mac_ip_len = 0; + tx_desc->vlan_cmd = cpu_to_le32(RNPM_TXD_CMD_EOP | RNPM_TXD_CMD_RS); + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. (Only applicable for weak-ordered + * memory model archs, such as IA-64). + * + * We also need this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + wmb(); + /* update tail */ + rnpm_wr_reg(tx_ring->tail, 0); + return 0; +} + +int rnpm_xmit_nop_frame_ring_temp(struct rnpm_adapter *adapter, + struct rnpm_ring *tx_ring) +{ + u16 i = tx_ring->next_to_use; + struct rnpm_tx_desc *tx_desc; + + tx_desc = RNPM_TX_DESC(tx_ring, i); + + /* set length to 0 */ + tx_desc->blen_mac_ip_len = 0; + tx_desc->vlan_cmd = cpu_to_le32(RNPM_TXD_CMD_EOP | RNPM_TXD_CMD_RS); + /* update tail */ + i++; + tx_desc++; + if (i == tx_ring->count) + i = 0; + tx_ring->next_to_use = i; + /* memory barrior */ + wmb(); + rnpm_wr_reg(tx_ring->tail, i); + /* no need clean */ + tx_ring->next_to_clean = i; + + return 0; +} + +/** + * rnpm_tx_maxrate_own - callback to set the maximum per-queue bitrate + * @netdev: network interface device structure + * @queue_index: Tx queue to set + * @maxrate: desired maximum transmit bitrate Mbps + **/ +static int rnpm_tx_maxrate_own(struct rnpm_adapter *adapter, int queue_index) +{ + struct rnpm_ring *tx_ring = adapter->tx_ring[queue_index]; + u64 real_rate = 0; + u32 maxrate = adapter->max_rate[queue_index]; + + if (!maxrate) + return rnpm_setup_tx_maxrate(adapter->hw.hw_addr, tx_ring, 0, + adapter->hw.usecstocount * + 1000000); + /* we need turn it to bytes/s */ + real_rate = (maxrate * 1024 * 1024) / 8; + rnpm_setup_tx_maxrate(adapter->hw.hw_addr, tx_ring, real_rate, + adapter->hw.usecstocount * 1000000); + + return 0; +} + +/** + * rnpm_configure_tx_ring - Configure 8259x Tx ring after Reset + * @adapter: board private structure + * @ring: structure containing ring specific data + * + * Configure the Tx descriptor ring after a reset. + **/ +void rnpm_configure_tx_ring(struct rnpm_adapter *adapter, + struct rnpm_ring *ring) +{ + struct rnpm_hw *hw = &adapter->hw; + // int i; + // u64 desc_dma_phy = ring->dma; + u8 queue_idx = ring->rnpm_queue_idx; + + wr32(hw, RNPM_DMA_REG_TX_DESC_BUF_BASE_ADDR_LO(queue_idx), + (u32)ring->dma); + wr32(hw, RNPM_DMA_REG_TX_DESC_BUF_BASE_ADDR_HI(queue_idx), + (u32)(((u64)ring->dma) >> 32) | (hw->pfvfnum << 24)); + wr32(hw, RNPM_DMA_REG_TX_DESC_BUF_LEN(queue_idx), ring->count); + + /* tail <= head */ + ring->next_to_clean = + rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD(queue_idx)); + ring->next_to_use = ring->next_to_clean; + ring->tail = hw->hw_addr + RNPM_DMA_REG_TX_DESC_BUF_TAIL(queue_idx); + rnpm_wr_reg(ring->tail, ring->next_to_use); + + // wr32(hw, RNPM_DMA_REG_TX_DESC_FETCH_CTRL(queue_idx), + // (64 << 0) /*max_water_flow*/ + // | (TSRN10_TX_DEFAULT_BURST << 16) + // /*max-num_descs_peer_read*/ + // ); + wr32(hw, RNPM_DMA_REG_TX_DESC_FETCH_CTRL(queue_idx), + (8 << 0) /*max_water_flow*/ + | (TSRN10_TX_DEFAULT_BURST << 16) + /*max-num_descs_peer_read*/ + ); + wr32(hw, RNPM_DMA_REG_TX_INT_DELAY_TIMER(queue_idx), + adapter->tx_usecs * hw->usecstocount); + wr32(hw, RNPM_DMA_REG_TX_INT_DELAY_PKTCNT(queue_idx), + adapter->tx_frames); + + rnpm_tx_maxrate_own(adapter, ring->queue_index); + // flow control: bytes-peer-ctrl-tm-clk. 0:no-control + /* reinitialize flowdirector state */ + if (adapter->flags & RNPM_FLAG_FDIR_HASH_CAPABLE) { + ring->atr_sample_rate = adapter->atr_sample_rate; + ring->atr_count = 0; + set_bit(__RNPM_TX_FDIR_INIT_DONE, &ring->state); + } else { + ring->atr_sample_rate = 0; + } + /* initialize XPS */ + if (!test_and_set_bit(__RNPM_TX_XPS_INIT_DONE, &ring->state)) { + struct rnpm_q_vector *q_vector = ring->q_vector; + + if (q_vector) + netif_set_xps_queue(adapter->netdev, + &q_vector->affinity_mask, + ring->queue_index); + } + + clear_bit(__RNPM_HANG_CHECK_ARMED, &ring->state); +} + +static void rnpm_setup_mtqc(struct rnpm_adapter *adapter) +{ + +} + +/** + * rnpm_configure_tx - Configure Transmit Unit after Reset + * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ +static void rnpm_configure_tx(struct rnpm_adapter *adapter) +{ + u32 i, dma_axi_ctl; + struct rnpm_hw *hw = &adapter->hw; + + rnpm_setup_mtqc(adapter); + + /* dma_axi_en.tx_en must be before Tx queues are enabled */ + dma_axi_ctl = rd32(hw, RNPM_DMA_AXI_EN); + dma_axi_ctl |= TX_AXI_RW_EN; + wr32(hw, RNPM_DMA_AXI_EN, dma_axi_ctl); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + for (i = 0; i < (adapter->num_tx_queues); i++) + rnpm_configure_tx_ring(adapter, adapter->tx_ring[i]); +} + +__maybe_unused static void +rnpm_rx_desc_queue_enable(struct rnpm_adapter *adapter, struct rnpm_ring *ring) +{ + +} + +void rnpm_disable_rx_queue(struct rnpm_adapter *adapter, struct rnpm_ring *ring) +{ + struct rnpm_hw *hw = &adapter->hw; + + wr32(hw, RNPM_DMA_RX_START(ring->rnpm_queue_idx), 0); +} + +void rnpm_configure_rx_ring(struct rnpm_adapter *adapter, + struct rnpm_ring *ring) +{ + struct rnpm_hw *hw = &adapter->hw; + u64 desc_phy = ring->dma; + u16 q_idx = ring->rnpm_queue_idx; + + /* disable queue to avoid issues while updating state */ + rnpm_disable_rx_queue(adapter, ring); + + /* set descripts registers*/ + wr32(hw, RNPM_DMA_REG_RX_DESC_BUF_BASE_ADDR_LO(q_idx), (u32)desc_phy); + wr32(hw, RNPM_DMA_REG_RX_DESC_BUF_BASE_ADDR_HI(q_idx), + ((u32)(desc_phy >> 32)) | (hw->pfvfnum << 24)); + wr32(hw, RNPM_DMA_REG_RX_DESC_BUF_LEN(q_idx), ring->count); + + ring->tail = hw->hw_addr + RNPM_DMA_REG_RX_DESC_BUF_TAIL(q_idx); + ring->next_to_clean = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD(q_idx)); + ring->next_to_use = ring->next_to_clean; + + wr32(hw, RNPM_DMA_REG_RX_DESC_FETCH_CTRL(q_idx), + 0 | (TSRN10_RX_DEFAULT_LINE << 0) /*rx-desc-flow*/ + | (TSRN10_RX_DEFAULT_BURST << 16) + /*max-read-desc-cnt*/ + ); + wr32(hw, RNPM_DMA_REG_RX_INT_DELAY_TIMER(q_idx), + adapter->rx_usecs * hw->usecstocount); + wr32(hw, RNPM_DMA_REG_RX_INT_DELAY_PKTCNT(q_idx), adapter->rx_frames); + rnpm_alloc_rx_buffers(ring, rnpm_desc_unused_rx(ring)); + /* enable receive descriptor ring */ + // wr32(hw, RNPM_DMA_RX_START(q_idx), 1); +} + +static void rnpm_configure_virtualization(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + // u8 *mac; + // u32 maclow, machi; + u32 ring, vfnum = 0; + // u8 port = adapter->port; + + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return; + + /* Enable only the PF's pool for Tx/Rx */ + + if (adapter->flags2 & RNPM_FLAG2_BRIDGE_MODE_VEB) { + wr32(hw, RNPM_DMA_CONFIG, + rd32(hw, RNPM_DMA_CONFIG) & (~DMA_VEB_BYPASS)); + adapter->flags2 |= RNPM_FLAG2_BRIDGE_MODE_VEB; + } + ring = adapter->tx_ring[0]->rnpm_queue_idx; + // enable find vf by dest-mac-address + wr32(hw, RNPM_HOST_FILTER_EN, 1); + wr32(hw, RNPM_REDIR_EN, 1); + wr32(hw, RNPM_MRQC_IOV_EN, RNPM_IOV_ENABLED); + wr32(hw, RNPM_ETH_DMAC_FCTRL, + rd32(hw, RNPM_ETH_DMAC_FCTRL) | RNPM_FCTRL_BROADCASE_BYPASS); + // wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, RNPM_MCSTCTRL_DMAC_47); + /* Map PF MAC address in RAR Entry 0 to first pool following VFs */ + hw->mac.ops.set_vmdq(hw, 0, ring / 2); + + adapter->vf_num_for_pf = 0x80 | vfnum; +} + +static void rnpm_set_rx_buffer_len(struct rnpm_adapter *adapter) +{ + // struct rnpm_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + int max_frame = netdev->mtu + ETH_HLEN + 2 * ETH_FCS_LEN; + struct rnpm_ring *rx_ring; + int i; + + if (max_frame < (ETH_FRAME_LEN + ETH_FCS_LEN)) + max_frame = (ETH_FRAME_LEN + ETH_FCS_LEN); + + for (i = 0; i < adapter->num_rx_queues; i++) { + rx_ring = adapter->rx_ring[i]; + clear_bit(__RNPM_RX_3K_BUFFER, &rx_ring->state); + clear_bit(__RNPM_RX_BUILD_SKB_ENABLED, &rx_ring->state); +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC + set_bit(__RNPM_RX_BUILD_SKB_ENABLED, &rx_ring->state); + hw_dbg(&adapter->hw, "set build skb\n"); + +#if (PAGE_SIZE < 8192) + if (RNPM_2K_TOO_SMALL_WITH_PADDING || + (max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN))) + ; + // set_bit(__RNPM_RX_3K_BUFFER, &rx_ring->state); +#endif + +#else /* !HAVE_SWIOTLB_SKIP_CPU_SYNC */ + /* FIXME */ + hw_dbg(&adapter->hw, "set construct skb\n"); + +#endif /* HAVE_SWIOTLB_SKIP_CPU_SYNC */ +#ifdef RNPM_OPTM_WITH_LPAGE + rx_ring->rx_page_buf_nums = RNPM_PAGE_BUFFER_NUMS(rx_ring); + // we can fixed 2k ? + rx_ring->rx_per_buf_mem = ALIGN( + (rnpm_rx_offset(rx_ring) + rnpm_rx_bufsz(rx_ring) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + RNPM_RX_HWTS_OFFSET), + 1024); +#endif + } +} + +/** + * rnpm_configure_rx - Configure 8259x Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Rx unit of the MAC after a reset. + **/ +static void rnpm_configure_rx(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + int i; + u32 rxctrl = 0, dma_axi_ctl; + +#if (PAGE_SIZE < 8192) + struct rnpm_ring *rx_ring = adapter->rx_ring[0]; +#endif + + + + /* set_rx_buffer_len must be called before ring initialization */ + rnpm_set_rx_buffer_len(adapter); + + /* Setup the HW Rx Head and Tail Descriptor Pointers and + * the Base and Length of the Rx Descriptor Ring + */ + for (i = 0; i < adapter->num_rx_queues; i++) + rnpm_configure_rx_ring(adapter, adapter->rx_ring[i]); + + if (adapter->pf_adapter->default_rx_ring > 0) { + wr32(hw, RNPM_ETH_DEFAULT_RX_RING, + adapter->pf_adapter->default_rx_ring); + } + +#if (PAGE_SIZE < 8192) + hw->dma_split_size = rnpm_rx_pg_size(rx_ring) / 2 - + rnpm_rx_offset(rx_ring) - + sizeof(struct skb_shared_info); +#endif + if (!hw->dma_split_size) + hw->dma_split_size = RNPM_RXBUFFER_1536; + /* dma split size need cal by skb headroom and tailroom */ +#define RNPM_DMA_RESPLIT_SIZE (hw->dma_split_size >> 4) + dbg("%s: dma_split_size=%d page_size=%d rx_page_size=%d rx_offset=%d skb_shared_info=%d\n", + __func__, hw->dma_split_size, PAGE_SIZE, rnpm_rx_pg_size(rx_ring), + rnpm_rx_offset(rx_ring), sizeof(struct skb_shared_info)); + rnpm_setup_dma_rx(adapter, RNPM_DMA_RESPLIT_SIZE); + + /* enable all receives */ + rxctrl |= 0; + + dma_axi_ctl = rd32(hw, RNPM_DMA_AXI_EN); + dma_axi_ctl |= RX_AXI_RW_EN; + wr32(hw, RNPM_DMA_AXI_EN, dma_axi_ctl); + + hw->mac.ops.enable_rx_dma(hw, rxctrl); +} + +#ifdef NETIF_F_HW_VLAN_CTAG_TX +static int rnpm_vlan_rx_add_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +#else /* !NETIF_F_HW_VLAN_CTAG_TX */ +static int rnpm_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +#endif /* NETIF_F_HW_VLAN_CTAG_TX */ +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + struct rnpm_hw *hw = &adapter->hw; + int port = 0; + unsigned long flags; + + if (hw->mac.vlan_location == rnpm_vlan_location_nic) { + if (hw->mac.ops.set_vfta) { + if (vid < VLAN_N_VID) { + set_bit(vid, adapter->active_vlans); + spin_lock_irqsave(&pf_adapter->vlan_setup_lock, + flags); + set_bit(vid, pf_adapter->active_vlans); + spin_unlock_irqrestore( + &pf_adapter->vlan_setup_lock, flags); + } + /* add VID to filter table */ + spin_lock_irqsave(&pf_adapter->vlan_setup_lock, flags); + hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), + true); + spin_unlock_irqrestore(&pf_adapter->vlan_setup_lock, + flags); + } + } else { + if (hw->mac.ops.set_vfta_mac) { + if (vid < VLAN_N_VID) + set_bit(vid, adapter->active_vlans); + hw->mac.ops.set_vfta_mac(&adapter->hw, vid, VMDQ_P(0), + true); + } + } + + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + u8 vfnum = RNPM_MAX_VF_CNT - 1; + + if (rd32(hw, RNPM_DMA_VERSION) >= 0x20201231) { + for (port = 0; port < 4; port++) + wr32(hw, RNPM_DMA_PORT_VEB_VID_TBL(port, vfnum), + vid); + } else { + wr32(hw, + RNPM_DMA_PORT_VEB_VID_TBL(adapter->port, vfnum), + vid); + } + } + return 0; +} + +static int rnpm_vlan_rx_kill_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter __maybe_unused *pf_adapter = adapter->pf_adapter; + struct rnpm_hw *hw = &adapter->hw; + unsigned long flags; + + if (!vid) + return 0; + + if (hw->mac.ops.set_vfta) { + /* remove VID from filter table only in no mutiport mode */ + if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) + hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), + false); + } + clear_bit(vid, adapter->active_vlans); + + if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) { + if (hw->mac.vlan_location == rnpm_vlan_location_nic) { + /* mutiport mode , only set update*/ + adapter->flags_feature |= + RNPM_FLAG_DELAY_UPDATE_VLAN_TABLE; + } else { + int i; + /* if use mac vlan table */ + /* clear hash table */ + wr32(&adapter->hw, RNPM_MAC_VLAN_HASH_TB(adapter->port), + 0); + /* update vlan hash table in mac */ + for_each_set_bit(i, adapter->active_vlans, + VLAN_N_VID) { + if (hw->mac.ops.set_vfta_mac) { + hw->mac.ops.set_vfta_mac(&adapter->hw, + i, VMDQ_P(0), + true); + } + } + rnpm_ncsi_set_vfta_mac_generic(hw); + } + + } else { + spin_lock_irqsave(&pf_adapter->vlan_setup_lock, flags); + clear_bit(vid, pf_adapter->active_vlans); + spin_unlock_irqrestore(&pf_adapter->vlan_setup_lock, flags); + } + + return 0; +} + + + +static u32 rnpm_vlan_filter_status_update(struct rnpm_pf_adapter *pf_adapter) +{ + int i; + u32 status = 1; + unsigned long flags; + + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + if (rnpm_port_is_valid(pf_adapter, i)) + status &= pf_adapter->vlan_filter_status[i]; + } + spin_lock_irqsave(&pf_adapter->vlan_filter_lock, flags); + pf_adapter->vlan_status_true = status; + spin_unlock_irqrestore(&pf_adapter->vlan_filter_lock, flags); + return status; +} + +/** + * rnpm_vlan_filter_disable - helper to disable hw vlan filtering + * @adapter: driver data + */ +static void __maybe_unused rnpm_vlan_filter_disable(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u8 port = adapter->port; + + pf_adapter->vlan_filter_status[port] = 0; + if (hw->mac.vlan_location == rnpm_vlan_location_nic) { + adapter->flags_feature |= RNPM_FLAG_DELAY_UPDATE_VLAN_FILTER; + /* off vlan filter if any port vlan filter off*/ + if (!rnpm_vlan_filter_status_update(pf_adapter)) + rnpm_vlan_filter_off(hw); + } else { + /* mac vlan filter is used */ + u32 value; + + value = rd32(hw, RNPM_MAC_PKT_FLT(port)); + value &= (~RNPM_VLAN_HASH_EN); + wr32(hw, RNPM_MAC_PKT_FLT(port), value); + rnpm_vlan_filter_off(hw); + } +} + +/** + * rnpm_vlan_filter_enable - helper to enable hw vlan filtering + * @adapter: driver data + */ +static void __maybe_unused rnpm_vlan_filter_enable(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u8 port = adapter->port; + + pf_adapter->vlan_filter_status[port] = 1; + /* open vlan filter if all port vlan filter on*/ + if (hw->mac.vlan_location == rnpm_vlan_location_nic) { + adapter->flags_feature |= RNPM_FLAG_DELAY_UPDATE_VLAN_FILTER; + if (rnpm_vlan_filter_status_update(pf_adapter)) + rnpm_vlan_filter_on(hw); + } else { + /* mac vlan filter is used */ + u32 value; + + value = rd32(hw, RNPM_MAC_PKT_FLT(port)); + value |= RNPM_VLAN_HASH_EN; + wr32(hw, RNPM_MAC_PKT_FLT(port), value); + + rnpm_vlan_filter_off(hw); + + // should set vlan tags registers? + } +} + +/** + * rnpm_vlan_strip_disable - helper to disable hw vlan stripping + * @adapter: driver data + */ +static void rnpm_vlan_strip_disable(struct rnpm_adapter *adapter) +{ + int i; + struct rnpm_ring *tx_ring; + struct rnpm_hw *hw = &adapter->hw; + + for (i = 0; i < adapter->num_rx_queues; i++) { + tx_ring = adapter->rx_ring[i]; + hw_queue_strip_rx_vlan(hw, tx_ring->rnpm_queue_idx, false); + } +} + +/** + * rnpm_vlan_strip_enable - helper to enable hw vlan stripping + * @adapter: driver data + */ +static void rnpm_vlan_strip_enable(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *tx_ring; + int i; + + for (i = 0; i < adapter->num_rx_queues; i++) { + tx_ring = adapter->rx_ring[i]; + hw_queue_strip_rx_vlan(hw, tx_ring->rnpm_queue_idx, true); + } +} + +static void rnpm_restore_vlan(struct rnpm_adapter *adapter) +{ + u16 vid; + struct rnpm_hw *hw = &adapter->hw; + + rnpm_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), 0); + for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID) + rnpm_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid); + + /* config vlan mode for mac */ + wr32(hw, RNPM_MAC_TX_VLAN_MODE(adapter->port), 0x00100000); +} + +/** + * rnpm_write_uc_addr_list - write unicast addresses to RAR table + * @netdev: network interface device structure + * + * Writes unicast address list to the RAR table. + * Returns: -ENOMEM on failure/insufficient address space + * 0 on no addresses written + * X on writing X addresses to the RAR table + **/ +static int rnpm_write_uc_addr_list(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + // unsigned int rar_entries = hw->mac.num_rar_entries - 1; + unsigned int rar_entries = adapter->uc_num - 1; + int count = 0; + + /* In SR-IOV mode significantly less RAR entries are available */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + rar_entries = RNPM_MAX_PF_MACVLANS - 1; + + /* return ENOMEM indicating insufficient memory for addresses */ + if (netdev_uc_count(netdev) > rar_entries) + return -ENOMEM; + + /* add offset */ + rar_entries += adapter->uc_off; + if (!netdev_uc_empty(netdev)) { + struct netdev_hw_addr *ha; + + hw_dbg(hw, "%s: rar_entries:%d, uc_count:%d offset %d\n", + __func__, rar_entries, adapter->uc_off, + netdev_uc_count(netdev)); + + /* return error if we do not support writing to RAR table */ + if (!hw->mac.ops.set_rar) + return -ENOMEM; + /* setup mac unicast filters */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + /* if use mac multicast */ + if (!hw->mac.ops.set_rar_mac) + return -ENOMEM; + } + + netdev_for_each_uc_addr(ha, netdev) { + if (!rar_entries) + break; + /* VMDQ_P(0) is num_vfs pf use the last vf in sriov mode */ + /* that's ok */ + hw->mac.ops.set_rar(hw, rar_entries, ha->addr, + VMDQ_P(0), RNPM_RAH_AV); + + /* if use mac filter we should also set Unicast to mac */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + hw->mac.ops.set_rar_mac( + hw, rar_entries - adapter->uc_off, + ha->addr, VMDQ_P(0), adapter->port); + } + rar_entries--; + count++; + } + } + /* write the addresses in reverse order to avoid write combining */ + + hw_dbg(hw, "%s: Clearing RAR[%d - %d]\n", __func__, adapter->uc_off + 1, + rar_entries); + for (; rar_entries > adapter->uc_off; rar_entries--) { + hw->mac.ops.clear_rar(hw, rar_entries); + if (hw->mac.mc_location == rnpm_mc_location_mac) { + hw->mac.ops.clear_rar_mac(hw, + rar_entries - adapter->uc_off, + adapter->port); + } + } + rnpm_ncsi_set_uc_addr_generic(hw); + + return count; +} + +static void rnpm_setup_fctrl(struct rnpm_hw *hw) +{ + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + int i; + u32 fctrl = 0; + + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + if (rnpm_port_is_valid(pf_adapter, i)) + fctrl |= pf_adapter->fctrl[i]; + } + wr32(hw, RNPM_ETH_DMAC_FCTRL, fctrl); +} + +/** + * rnpm_set_rx_mode - Unicast, Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_rx_method entry point is called whenever the unicast/multicast + * address list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper unicast, multicast and + * promiscuous mode. + **/ +void rnpm_set_rx_mode(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + struct rnpm_hw *hw = &adapter->hw; + u32 fctrl; + u32 fctrl_mac = 0; + netdev_features_t __maybe_unused features = netdev->features; + int count; + u8 port = adapter->port; + + fctrl = pf_adapter->fctrl[port]; + + // mcstctrl = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + + /* clear the bits we are changing the status of */ + fctrl &= ~(RNPM_FCTRL_UPE | RNPM_FCTRL_MPE); + + /* promisc mode */ + if (netdev->flags & IFF_PROMISC) { + hw->addr_ctrl.user_set_promisc = true; + fctrl |= (RNPM_FCTRL_UNICASE_BYPASS | + RNPM_FCTRL_MULTICASE_BYPASS | + RNPM_FCTRL_BROADCASE_BYPASS); + fctrl_mac |= RNPM_RX_ALL; + /* disable hardware filter vlans in promisc mode */ + features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; + features &= ~NETIF_F_HW_VLAN_CTAG_RX; + } else { + if (netdev->flags & IFF_ALLMULTI) { + fctrl |= RNPM_FCTRL_MULTICASE_BYPASS; + fctrl_mac |= RNPM_RX_ALL_MUL; + // mcstctrl &= ~(RNPM_MCSTCTRL_MULTICASE_TBL_EN); + } else { + /* Write addresses to the MTA, if the attempt fails + * then we should just turn on promiscuous mode so + * that we can at least receive multicast traffic + */ + count = hw->mac.ops.update_mc_addr_list(hw, netdev); + if (count < 0) { + fctrl |= RNPM_FCTRL_MPE; + fctrl_mac |= RNPM_RX_ALL_MUL; + // mcstctrl &= ~RNPM_MCSTCTRL_MULTICASE_TBL_EN; + } else if (count) { + // mcstctrl |= RNPM_MCSTCTRL_MULTICASE_TBL_EN; + } + } + hw->addr_ctrl.user_set_promisc = false; + } + + // test mode + // fctrl_mac |= RNPM_RX_ALL; + /* Write addresses to available RAR registers, if there is not + * sufficient space to store all the addresses then enable + * unicast promiscuous mode + */ + if (rnpm_write_uc_addr_list(netdev) < 0) { + fctrl |= RNPM_FCTRL_UPE; + // mcstctrl &= ~RNPM_MCSTCTRL_UNICASE_TBL_EN; + } + + if (adapter->num_vfs) + rnpm_restore_vf_multicasts(adapter); + + // force disable Multicast filter why? + // fctrl |= RNPM_FCTRL_MULTICASE_BYPASS; + // update multicase & unicast regs + if (hw->mac.mc_location == rnpm_mc_location_mac) { + u32 value; + + value = rd32(hw, RNPM_MAC_PKT_FLT(port)); + if (!(adapter->flags & + RNPM_FLAG_SWITCH_LOOPBACK_EN)) { // switch-loopback mode mac + // should rece all pkgs + value &= ~(RNPM_RX_ALL | RNPM_RX_ALL_MUL); + } + value |= fctrl_mac; + wr32(hw, RNPM_MAC_PKT_FLT(port), value); + /* in this mode should always close nic mc uc */ + fctrl |= RNPM_FCTRL_MULTICASE_BYPASS; + fctrl |= RNPM_FCTRL_UNICASE_BYPASS; + wr32(hw, RNPM_ETH_DMAC_FCTRL, fctrl); + } else { + pf_adapter->fctrl[port] = fctrl; + + rnpm_setup_fctrl(hw); + } + + if (features & NETIF_F_HW_VLAN_CTAG_FILTER) + rnpm_vlan_filter_enable(adapter); + else + rnpm_vlan_filter_disable(adapter); + + if (features & NETIF_F_HW_VLAN_CTAG_RX) + rnpm_vlan_strip_enable(adapter); + else + rnpm_vlan_strip_disable(adapter); +} + +static void rnpm_napi_enable_all(struct rnpm_adapter *adapter) +{ + int q_idx; + + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) + napi_enable(&adapter->q_vector[q_idx]->napi); +} + +static bool rnpm_wait_irq_miss_check_done(struct rnpm_adapter *adapter) +{ + int q_idx; + + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { + if (test_bit(RNPM_IRQ_MISS_HANDLE_DONE, + &adapter->q_vector[q_idx]->flags)) + return false; + } + + return true; +} + +static void rnpm_napi_disable_all(struct rnpm_adapter *adapter) +{ + int q_idx; + + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { + /* stop timer avoid error */ + rnpm_htimer_stop(adapter->q_vector[q_idx]); + + napi_disable(&adapter->q_vector[q_idx]->napi); + } +} + +#ifdef CONFIG_RNPM_DCB +/** + * rnpm_configure_dcb - Configure DCB hardware + * @adapter: rnpm adapter struct + * + * This is called by the driver on open to configure the DCB hardware. + * This is also called by the gennetlink interface when reconfiguring + * the DCB state. + */ +static void rnpm_configure_dcb(struct rnpm_adapter *adapter) +{ + +} +#endif + +/* Additional bittime to account for RNPM framing */ +#define RNPM_ETH_FRAMING 20 + +/** + * rnpm_hpbthresh - calculate high water mark for flow control + * + * @adapter: board private structure to calculate for + * @pb: packet buffer to calculate + */ +__maybe_unused static int rnpm_hpbthresh(struct rnpm_adapter *adapter, int pb) +{ + int marker = 0; + + return marker; +} + +/** + * rnpm_lpbthresh - calculate low water mark for flow control + * + * @adapter: board private structure to calculate for + * @pb: packet buffer to calculate + */ +__maybe_unused static int rnpm_lpbthresh(struct rnpm_adapter *adapter) +{ + + return 0; +} + +/* rnpm_pbthresh_setup - calculate and setup high low water marks */ +__maybe_unused static void rnpm_pbthresh_setup(struct rnpm_adapter *adapter) +{ + +} + +static void rnpm_configure_pb(struct rnpm_adapter *adapter) +{ + +} + +static void rnpm_fdir_filter_restore(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct hlist_node *node2; + struct rnpm_fdir_filter *filter; + unsigned long flags; + + spin_lock_irqsave(&adapter->fdir_perfect_lock, flags); + + /* enable tcam if set tcam mode */ + if (adapter->fdir_mode == fdir_mode_tcam) { + wr32(hw, RNPM_ETH_TCAM_EN, 1); + wr32(hw, RNPM_TOP_ETH_TCAM_CONFIG_ENABLE, 1); + wr32(hw, RNPM_TCAM_CACHE_ENABLE, 1); + } + + /* setup ntuple */ + hlist_for_each_entry_safe(filter, node2, &adapter->fdir_filter_list, + fdir_node) { + rnpm_fdir_write_perfect_filter( + adapter->fdir_mode, hw, &filter->filter, filter->hw_idx, + (filter->action == RNPM_FDIR_DROP_QUEUE) ? + RNPM_FDIR_DROP_QUEUE : + adapter->rx_ring[filter->action] + ->rnpm_queue_idx); + } + + spin_unlock_irqrestore(&adapter->fdir_perfect_lock, flags); +} + +__maybe_unused static void rnpm_configure_pause(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + + hw->mac.ops.fc_enable(hw); +} + +void rnpm_vlan_stags_flag(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + u8 port = adapter->port; + + /* stags is added */ + if (adapter->flags2 & RNPM_FLAG2_VLAN_STAGS_ENABLED) { + /* low 16bits should not all zero */ + // wr32(hw, RNPM_MAC_TX_VLAN_TAG(port), 0xc60ffff); + wr32(hw, RNPM_MAC_TX_VLAN_TAG(port), + RNPM_ERIVLT | RNPM_EDVLP | RNPM_ETV | + (RNPM_EVLS_ALWAYS_STRIP << RNPM_EVLS_OFFSET) | + RNPM_VL_MODE_OFF); + // wr32(hw, RNPM_MAC_TX_VLAN_MODE(port), 0x180000); + wr32(hw, RNPM_MAC_TX_VLAN_MODE(port), 0x180000); + wr32(hw, RNPM_MAC_INNER_VLAN_INCL(port), 0x100000); + } else { + /* low 16bits should not all zero */ + // wr32(hw, RNPM_MAC_TX_VLAN_TAG(port), 0x200ffff); + wr32(hw, RNPM_MAC_TX_VLAN_TAG(port), + RNPM_VTHM | RNPM_VL_MODE_ON | RNPM_ETV); + wr32(hw, RNPM_MAC_TX_VLAN_MODE(port), 0x100000); + wr32(hw, RNPM_MAC_INNER_VLAN_INCL(port), 0x100000); + } +} + +static void rnpm_configure(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + + rnpm_configure_pb(adapter); // setup high low water + +#ifdef CONFIG_RNPM_DCB + rnpm_configure_dcb(adapter); +#endif + + /* We must restore virtualization before VLANs or else + * the VLVF registers will not be populated + */ + rnpm_configure_virtualization(adapter); + /* init setup pause */ + hw->mac.ops.setup_fc(hw); + // rnpm_configure_pause(adapter); + /* Unicast, Multicast and Promiscuous mode set */ + rnpm_set_rx_mode(adapter->netdev); + + /* reset unicast address */ + hw->mac.ops.set_rar(hw, adapter->uc_off, hw->mac.addr, VMDQ_P(0), + RNPM_RAH_AV); + + /* setup mac unicast filters */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + hw->mac.ops.set_rar_mac(hw, 0, hw->mac.addr, VMDQ_P(0), + adapter->port); + } + /* what conditions should restore vlan ? */ + rnpm_restore_vlan(adapter); + /* setup rss key and table */ + /* enable all eth filter */ + wr32(hw, RNPM_HOST_FILTER_EN, 1); + /* open redir */ + wr32(hw, RNPM_REDIR_EN, 1); + // rnpm_init_rss_key(adapter); + rnpm_init_rss_table(adapter); + + /* open sctp check en */ + if (hw->feature_flags & RNPM_NET_FEATURE_RX_CHECKSUM) + wr32(hw, RNPM_ETH_SCTP_CHECKSUM_EN, 1); + rnpm_vlan_stags_flag(adapter); + + if (adapter->flags & RNPM_FLAG_FDIR_HASH_CAPABLE) { + // rnpm_init_fdir_signature_n10(&adapter->hw, adapter->fdir_pballoc); + } else if (adapter->flags & RNPM_FLAG_FDIR_PERFECT_CAPABLE) { + // rnpm_init_fdir_perfect_n10(&adapter->hw, adapter->fdir_pballoc); + rnpm_fdir_filter_restore(adapter); + } + + if (hw->dma_version >= 0x20210108) { + // mark Multicast as broadcast + wr32(hw, RNPM_VEB_MAC_MASK_LO, 0xffffffff); + wr32(hw, RNPM_VEB_MAC_MASK_HI, 0xfeff); + } + + rnpm_configure_tx(adapter); + rnpm_configure_rx(adapter); +} + +static inline bool rnpm_is_sfp(struct rnpm_hw *hw) +{ + // return false; + return true; +} + +/** + * rnpm_sfp_link_config - set up SFP+ link + * @adapter: pointer to private adapter struct + **/ +static void rnpm_sfp_link_config(struct rnpm_adapter *adapter) +{ + /* We are assuming the worst case scenario here, and that + * is that an SFP was inserted/removed after the reset + * but before SFP detection was enabled. As such the best + * solution is to just start searching as soon as we start + */ + adapter->flags2 |= RNPM_FLAG2_SFP_NEEDS_RESET; +} + +/** + * rnpm_non_sfp_link_config - set up non-SFP+ link + * @hw: pointer to private hardware struct + * + * Returns 0 on success, negative on failure + **/ +static int rnpm_non_sfp_link_config(struct rnpm_hw *hw) +{ + u32 ret = RNPM_ERR_LINK_SETUP; + + // ret = hw->mac.ops.setup_link(hw, hw->phy.autoneg_advertised, true); + + return ret; +} + +void control_mac_rx(struct rnpm_adapter *adapter, bool on) +{ + struct rnpm_hw *hw = &adapter->hw; + u8 port = adapter->port; + u32 value = 0; + u32 count = 0; + + if (on) { + wr32(hw, RNPM_ETH_RX_PROGFULL_THRESH_PORT(adapter->port), + RECEIVE_ALL_THRESH); + do { + wr32(hw, RNPM_MAC_RX_CFG(port), + rd32(hw, RNPM_MAC_RX_CFG(port)) | 0x01 | + RNPM_MAX_RX_CFG_IPC); + usleep_range(100, 200); + value = rd32(hw, RNPM_MAC_RX_CFG(port)); + count++; + if (count > 1000) + break; + } while (!(value & 0x01)); + + // clean loop back + do { + wr32(hw, RNPM_MAC_RX_CFG(port), + (rd32(hw, RNPM_MAC_RX_CFG(port)) & (~0x400)) | + RNPM_MAX_RX_CFG_IPC); + usleep_range(100, 200); + value = rd32(hw, RNPM_MAC_RX_CFG(port)); + count++; + if (count > 1000) + break; + } while (value & 0x400); + + /* in this mode close mc filter in mac */ + if (hw->mac.mc_location == rnpm_mc_location_nic) + wr32(hw, RNPM_MAC_PKT_FLT(port), + rd32(hw, RNPM_MAC_PKT_FLT(port)) | RNPM_RA); + else + wr32(hw, RNPM_MAC_PKT_FLT(port), + rd32(hw, RNPM_MAC_PKT_FLT(port)) | RNPM_HPF); + } else { + wr32(hw, RNPM_ETH_RX_PROGFULL_THRESH_PORT(adapter->port), + DROP_ALL_THRESH); + // set loopback + do { + wr32(hw, RNPM_MAC_RX_CFG(port), + rd32(hw, RNPM_MAC_RX_CFG(port)) | 0x400 | + RNPM_MAX_RX_CFG_IPC); + usleep_range(100, 200); + value = rd32(hw, RNPM_MAC_RX_CFG(port)); + count++; + if (count > 1000) { + netdev_dbg(adapter->netdev, + "setup rx on timeout\n"); + break; + } + } while (!(value & 0x400)); + } +} + +static void rnpm_up_complete(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + int err; + int i; + + control_mac_rx(adapter, false); + rnpm_get_hw_control(adapter); + rnpm_configure_msix(adapter); + + /* enable the optics for n10 SFP+ fiber */ + if (hw->mac.ops.enable_tx_laser) + hw->mac.ops.enable_tx_laser(hw); + /* memory barrier */ + smp_mb__before_atomic(); + clear_bit(__RNPM_DOWN, &adapter->state); + rnpm_napi_enable_all(adapter); + + if (rnpm_is_sfp(hw)) { + rnpm_sfp_link_config(adapter); + } else { + err = rnpm_non_sfp_link_config(hw); + if (err) + e_err(probe, "link_config FAILED %d\n", err); + } + /*clear any pending interrupts*/ + rnpm_irq_enable(adapter); + + /* enable transmits */ + netif_tx_start_all_queues(adapter->netdev); + + /* enable rx transmit */ + for (i = 0; i < adapter->num_rx_queues; i++) + wr32(hw, RNPM_DMA_RX_START(adapter->rx_ring[i]->rnpm_queue_idx), + 1); + adapter->link_check_timeout = jiffies; + mod_timer(&adapter->service_timer, HZ + jiffies); + rnpm_mbx_ifup_down(&adapter->hw, MBX_IFUP); + // control_mac_rx(adapter, true); + /* Set PF Reset Done bit so PF/VF Mail Ops can work */ + rnpm_mbx_lane_link_changed_event_enable(&adapter->hw, true); +} + +void rnpm_reinit_locked(struct rnpm_adapter *adapter) +{ + WARN_ON(in_interrupt()); + /* put off any impending NetWatchDogTimeout */ + // adapter->netdev->trans_start = jiffies; + + while (test_and_set_bit(__RNPM_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + rnpm_down(adapter); + /* If SR-IOV enabled then wait a bit before bringing the adapter + * back up to give the VFs time to respond to the reset. The + * two second wait is based upon the watchdog timer cycle in + * the VF driver. + */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + msleep(2000); + rnpm_up(adapter); + clear_bit(__RNPM_RESETTING, &adapter->state); +} + +void rnpm_up(struct rnpm_adapter *adapter) +{ + /* hardware has been reset, we need to reload some things */ + rnpm_configure(adapter); + + rnpm_up_complete(adapter); +} + +void rnpm_reset(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + int err; + + /* lock SFP init bit to prevent race conditions with the watchdog */ + while (test_and_set_bit(__RNPM_IN_SFP_INIT, &adapter->state)) + usleep_range(1000, 2000); + + /* clear all SFP and link config related flags while holding SFP_INIT */ + adapter->flags2 &= + ~(RNPM_FLAG2_SEARCH_FOR_SFP | RNPM_FLAG2_SFP_NEEDS_RESET); + adapter->flags &= ~RNPM_FLAG_NEED_LINK_CONFIG; + + err = hw->mac.ops.init_hw(hw); + if (err) { + e_dev_err("init_hw: Hardware Error: err:%d. line:%d\n", err, + __LINE__); + } + + clear_bit(__RNPM_IN_SFP_INIT, &adapter->state); + + /* reprogram the RAR[0] in case user changed it. */ + hw->mac.ops.set_rar(hw, adapter->uc_off, hw->mac.addr, VMDQ_P(0), + RNPM_RAH_AV); + /* setup mac unicast filters */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + hw->mac.ops.set_rar_mac(hw, 0, hw->mac.addr, VMDQ_P(0), + adapter->port); + } + +#ifndef NO_PTP + if (module_enable_ptp) { + if (adapter->flags2 & RNPM_FLAG2_PTP_ENABLED && + (adapter->ptp_rx_en || adapter->ptp_tx_en)) + rnpm_ptp_reset(adapter); + } +#endif +} + +#ifdef RNPM_OPTM_WITH_LPAGE +/** + * rnpm_clean_rx_ring - Free Rx Buffers per Queue + * @rx_ring: ring to free buffers from + **/ +static void rnpm_clean_rx_ring(struct rnpm_ring *rx_ring) +{ + u16 i = rx_ring->next_to_clean; + struct rnpm_rx_buffer *rx_buffer; + + if (!rx_ring->rx_buffer_info) + return; + if (rx_ring->skb) + dev_kfree_skb(rx_ring->skb); + + rx_ring->skb = NULL; + rx_buffer = &rx_ring->rx_buffer_info[i]; + /* Free all the Rx ring sk_buffs */ + while (i != rx_ring->next_to_alloc) { + if (!rx_buffer->page) + goto next_buffer; + + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, + rx_buffer->page_offset, + rnpm_rx_bufsz(rx_ring), + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + rnpm_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + /* now this page is not used */ + rx_buffer->page = NULL; +next_buffer: + i++; + rx_buffer++; + if (i == rx_ring->count) { + i = 0; + rx_buffer = rx_ring->rx_buffer_info; + } + } + +#ifdef HAVE_AF_XDP_ZC_SUPPORT +// skip_free: +#endif + rx_ring->next_to_alloc = 0; + rx_ring->next_to_clean = 0; + rx_ring->next_to_use = 0; +} +#else +/** + * rnpm_clean_rx_ring - Free Rx Buffers per Queue + * @rx_ring: ring to free buffers from + **/ +static void rnpm_clean_rx_ring(struct rnpm_ring *rx_ring) +{ + u16 i = rx_ring->next_to_clean; + struct rnpm_rx_buffer *rx_buffer = &rx_ring->rx_buffer_info[i]; + + /* Free all the Rx ring sk_buffs */ + while (i != rx_ring->next_to_alloc) { + if (rx_buffer->skb) { + struct sk_buff *skb = rx_buffer->skb; + /* no need this */ + if (RNPM_CB(skb)->page_released) + dma_unmap_page_attrs(rx_ring->dev, + RNPM_CB(skb)->dma, + rnpm_rx_pg_size(rx_ring), + DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + dev_kfree_skb(skb); + rx_buffer->skb = NULL; + } + + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, + rx_buffer->page_offset, + rnpm_rx_bufsz(rx_ring), + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + rnpm_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + RNPM_RX_DMA_ATTR); + + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + /* now this page is not used */ + rx_buffer->page = NULL; + i++; + rx_buffer++; + if (i == rx_ring->count) { + i = 0; + rx_buffer = rx_ring->rx_buffer_info; + } + } + + rx_ring->next_to_alloc = 0; + rx_ring->next_to_clean = 0; + rx_ring->next_to_use = 0; +} +#endif +/** + * rnpm_clean_tx_ring - Free Tx Buffers + * @tx_ring: ring to be cleaned + **/ +static void rnpm_clean_tx_ring(struct rnpm_ring *tx_ring) +{ + unsigned long size; + u16 i = tx_ring->next_to_clean; + struct rnpm_tx_buffer *tx_buffer = &tx_ring->tx_buffer_info[i]; + + BUG_ON(tx_ring == NULL); + /* ring already cleared, nothing to do */ + if (!tx_ring->tx_buffer_info) + return; + while (i != tx_ring->next_to_use) { + struct rnpm_tx_desc *eop_desc, *tx_desc; + + dev_kfree_skb_any(tx_buffer->skb); + /* unmap skb header data */ + dma_unmap_single(tx_ring->dev, dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); + + eop_desc = tx_buffer->next_to_watch; + tx_desc = RNPM_TX_DESC(tx_ring, i); + /* unmap remaining buffers */ + while (tx_desc != eop_desc) { + tx_buffer++; + tx_desc++; + i++; + if (unlikely(i == tx_ring->count)) { + i = 0; + tx_buffer = tx_ring->tx_buffer_info; + tx_desc = RNPM_TX_DESC(tx_ring, 0); + } + + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buffer, len)) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer, len, 0); + } + } + /* move us one more past the eop_desc for start of next pkt */ + tx_buffer++; + i++; + if (unlikely(i == tx_ring->count)) { + i = 0; + tx_buffer = tx_ring->tx_buffer_info; + } + } + + netdev_tx_reset_queue(txring_txq(tx_ring)); + + size = sizeof(struct rnpm_tx_buffer) * tx_ring->count; + memset(tx_ring->tx_buffer_info, 0, size); + + /* Zero out the descriptor ring */ + memset(tx_ring->desc, 0, tx_ring->size); + + tx_ring->next_to_use = 0; + tx_ring->next_to_clean = 0; +} + +/** + * rnpm_clean_all_rx_rings - Free Rx Buffers for all queues + * @adapter: board private structure + **/ +static void rnpm_clean_all_rx_rings(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_rx_queues; i++) + rnpm_clean_rx_ring(adapter->rx_ring[i]); +} + +/** + * rnpm_clean_all_tx_rings - Free Tx Buffers for all queues + * @adapter: board private structure + **/ +static void rnpm_clean_all_tx_rings(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_tx_queues; i++) + rnpm_clean_tx_ring(adapter->tx_ring[i]); +} + +static void rnpm_fdir_filter_exit(struct rnpm_adapter *adapter) +{ + struct hlist_node *node2; + struct rnpm_fdir_filter *filter; + struct rnpm_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->fdir_perfect_lock, flags); + + hlist_for_each_entry_safe(filter, node2, &adapter->fdir_filter_list, + fdir_node) { + /* call earase to hw */ + rnpm_fdir_erase_perfect_filter(adapter->fdir_mode, hw, + &filter->filter, filter->hw_idx); + + hlist_del(&filter->fdir_node); + kfree(filter); + } + adapter->fdir_filter_count = 0; + + spin_unlock_irqrestore(&adapter->fdir_perfect_lock, flags); +} + +void rnpm_down(struct rnpm_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct rnpm_hw *hw = &adapter->hw; + int i, retry = 200; + + rnpm_dbg("%s %s port=%d!!!\n", netdev->name, __func__, adapter->port); + rnpm_logd(LOG_FUNC_ENTER, "enter %s %s\n", __func__, + adapter->netdev->name); + /* signal that we are down to the interrupt handler */ + set_bit(__RNPM_DOWN, &adapter->state); + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); + netif_tx_disable(netdev); + control_mac_rx(adapter, false); + rnpm_mbx_ifup_down(&adapter->hw, MBX_IFDOWN); + rnpm_link_stat_mark(hw, hw->nr_lane, 0); + // wait all packets loop back + usleep_range(10000, 20000); + + /* disable all enabled rx queues */ + for (i = 0; i < adapter->num_rx_queues; i++) { + rnpm_disable_rx_queue(adapter, adapter->rx_ring[i]); + /* only handle when srio enable or mutiport mode and change rx length + * setup + */ + if (((adapter->flags & RNPM_FLAG_SRIOV_ENABLED) || + (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) && + (adapter->rx_ring[i]->ring_flags & + RNPM_RING_FLAG_CHANGE_RX_LEN)) { + int head; + + head = rd32( + hw, + RNPM_DMA_REG_RX_DESC_BUF_HEAD( + adapter->rx_ring[i]->rnpm_queue_idx)); + adapter->rx_ring[i]->ring_flags &= + (~RNPM_RING_FLAG_CHANGE_RX_LEN); + /* we should delay setup rx length to wait rx head to 0 */ + if (head >= adapter->rx_ring[i]->reset_count) { + adapter->rx_ring[i]->ring_flags |= + RNPM_RING_FLAG_DELAY_SETUP_RX_LEN; + /* set sw count to head + 1*/ + adapter->rx_ring[i]->temp_count = head + 1; + } + } + /* only down without rx_len change no need handle */ + } + rnpm_irq_disable(adapter); + rnpm_napi_disable_all(adapter); + + adapter->flags2 &= + ~(RNPM_FLAG2_FDIR_REQUIRES_REINIT | RNPM_FLAG2_RESET_REQUESTED); + adapter->flags &= ~RNPM_FLAG_NEED_LINK_UPDATE; + + if (adapter->num_vfs) { + /* ping all the active vfs to let them know we are going down */ + rnpm_ping_all_vfs(adapter); + + /* Disable all VFTE/VFRE TX/RX */ + rnpm_disable_tx_rx(adapter); + } + del_timer_sync(&adapter->service_timer); + // maybe bug here if call tx real hang reset + cancel_work_sync(&adapter->service_task); + + while (retry) { + if (rnpm_wait_irq_miss_check_done(adapter)) + break; + retry--; + usleep_range(20000, 40000); + } + if (!retry) { + netdev_dbg(adapter->netdev, + "error: %s wait ire miss check done timeout\n", + netdev->name); + } + + /* disable transmits in the hardware now that interrupts are off */ + for (i = 0; i < adapter->num_tx_queues; i++) { + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_ring *tx_ring = adapter->tx_ring[i]; + int count = tx_ring->count; + int head, tail; + int timeout = 0; + u32 status = 0; + /* 1. stop queue */ + // check tx ready + do { + status = rd32( + hw, RNPM_DMA_TX_READY(tx_ring->rnpm_queue_idx)); + usleep_range(1000, 2000); + timeout++; + rnpm_dbg("wait %d tx ready to 1\n", + tx_ring->rnpm_queue_idx); + } while ((status != 1) && (timeout < 100)); + + if (timeout >= 100) { + head = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD( + tx_ring->rnpm_queue_idx)); + + tail = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL( + tx_ring->rnpm_queue_idx)); + netdev_dbg( + adapter->netdev, + "wait tx ready timeout, name=%s, i=%d queue_idx=%d head=%d tail=%d\n", + netdev->name, i, tx_ring->rnpm_queue_idx, head, + tail); + } + + /* 2. try to set tx head to 0 in sriov mode since we don't reset + * in sriov on or mutiport mode + */ + if ((adapter->flags & RNPM_FLAG_SRIOV_ENABLED) || + (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) { + head = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD( + tx_ring->rnpm_queue_idx)); + if (head != 0) { + u16 next_to_use = tx_ring->next_to_use; + + if (head != (count - 1)) { + /* 3 set len head + 1 */ + wr32(hw, + RNPM_DMA_REG_TX_DESC_BUF_LEN( + tx_ring->rnpm_queue_idx), + head + 1); + // tx_ring->count = head + 1; + } + /* set to use head */ + tx_ring->next_to_use = head; + /* 4 send a len zero packet */ + rnpm_xmit_nop_frame_ring(adapter, tx_ring); + // wr32(hw, RNPM_DMA_TX_START(tx_ring->rnpm_queue_idx), 1); + /* 5 wait head to zero */ + while ((head != 0) && (timeout < 1000)) { + head = rd32( + hw, + RNPM_DMA_REG_TX_DESC_BUF_HEAD( + tx_ring->rnpm_queue_idx)); + usleep_range(10000, 20000); + timeout++; + } + if (timeout >= 1000) { + rnpm_dbg( + "[%s] Wait Rx-ring %d head to zero time out\n", + netdev->name, + tx_ring->rnpm_queue_idx); + } + /* 6 stop queue again*/ + // wr32(hw, RNPM_DMA_TX_START(tx_ring->rnpm_queue_idx), 0); + + /* 7 write back next_to_use maybe hw hang */ + tx_ring->next_to_use = next_to_use; + } + } + } + + if (!pci_channel_offline(adapter->pdev)) { + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED) && + (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED))) { + rnpm_reset(adapter); + } + } + + /* power down the optics for n10 SFP+ fiber */ + if (hw->mac.ops.disable_tx_laser) + hw->mac.ops.disable_tx_laser(hw); + + rnpm_clean_all_tx_rings(adapter); + rnpm_clean_all_rx_rings(adapter); + + if (hw->ncsi_en) + control_mac_rx(adapter, true); + + rnpm_logd(LOG_FUNC_ENTER, "exit %s %s\n", __func__, + adapter->netdev->name); +} + +/** + * rnpm_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + **/ +static void rnpm_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + /* Do the reset outside of interrupt context */ + int i; + bool real_tx_hang = false; + +#define TX_TIMEO_LIMIT 16000 + for (i = 0; i < adapter->num_tx_queues; i++) { + struct rnpm_ring *tx_ring = adapter->tx_ring[i]; + + if (check_for_tx_hang(tx_ring) && rnpm_check_tx_hang(tx_ring)) + real_tx_hang = true; + } + + if (real_tx_hang) { + /* Do the reset outside of interrupt context */ + e_info(drv, "tx real hang\n"); + rnpm_tx_timeout_reset(adapter); + } else { + e_info(drv, + "Fake Tx hang detected with timeout of %d seconds\n", + netdev->watchdog_timeo / HZ); + + /* fake Tx hang - increase the kernel timeout */ + if (netdev->watchdog_timeo < TX_TIMEO_LIMIT) + netdev->watchdog_timeo *= 2; + } +} + +/** + * rnpm_sw_init - Initialize general software structures (struct rnpm_adapter) + * @adapter: board private structure to initialize + * + * rnpm_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ +static int rnpm_sw_init(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + unsigned int rss = 0, fdir; + int i; +#ifdef CONFIG_RNPM_DCB + int j; + struct tc_configuration *tc; +#endif + + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_device_id = pdev->subsystem_device; + + /* Set common capability flags and settings */ + if (hw->rss_type == rnpm_rss_uv3p) { + /* Makefile use RNPM_MAX_RINGS to limit ring number */ + rss = min_t(int, adapter->max_ring_pair_counts, + num_online_cpus()); + } else { + rss = min_t(int, adapter->max_ring_pair_counts, + num_online_cpus()); + } +#ifdef RNPM_DEFAULT_RINGS_CNT + rss = min_t(int, rss, RNPM_DEFAULT_RINGS_CNT); +#endif + // should limit queue since cpu maybe large than vectors number + rss = min_t(int, rss, adapter->max_msix_counts); + adapter->ring_feature[RING_F_RSS].limit = + min_t(int, rss, adapter->max_ring_pair_counts); + + // adapter->flags2 |= RNPM_FLAG2_RSC_CAPABLE; + // adapter->flags2 |= RNPM_FLAG2_RSC_ENABLED; + adapter->atr_sample_rate = 20; + +#ifdef RNPM_MAX_RINGS + fdir = min_t(int, 32, RNPM_MAX_RINGS); +#else + fdir = min_t(int, 32, num_online_cpus()); +#endif + // no-use this ? + adapter->ring_feature[RING_F_FDIR].limit = fdir; + + if (hw->feature_flags & RNPM_NET_FEATURE_RX_NTUPLE_FILTER) { + spin_lock_init(&adapter->fdir_perfect_lock); + /* init count record */ + adapter->fdir_filter_count = 0; + adapter->layer2_count = 0; + adapter->tuple_5_count = 0; + if (hw->feature_flags & RNPM_NET_FEATURE_TCAM) + adapter->fdir_mode = fdir_mode_tcam; + else + adapter->fdir_mode = fdir_mode_tuple5; + + adapter->fdir_pballoc = + adapter->layer2_count_max + adapter->tuple_5_count_max; + // adapter->flags |= RNPM_FLAG_FDIR_PERFECT_CAPABLE; + } + /* itr sw setup here */ + adapter->sample_interval = RNPM_DEFAULT_SAMPLE_INTERVAL; + adapter->adaptive_rx_coal = RNPM_DEFAULT_ENABLE; + adapter->adaptive_tx_coal = RNPM_DEFAULT_ENABLE; + adapter->auto_rx_coal = RNPM_DEFAULT_DISABLE; + adapter->napi_budge = RNPM_DEFAULT_NAPI_BUDGE; + + /* set default work limits */ + adapter->tx_work_limit = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->coalesce.tx_work_limit; + adapter->rx_usecs = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->coalesce.rx_usecs; + adapter->rx_frames = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->coalesce.rx_frames; + adapter->tx_usecs = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->coalesce.tx_usecs; + adapter->tx_frames = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->coalesce.tx_frames; + if (rnpm_info_tbl[adapter->pf_adapter->board_type]->mac_padding) + adapter->priv_flags |= RNPM_PRIV_FLAG_TX_PADDING; + + /* Set MAC specific capability flags and exceptions */ + /* port capability is set here */ + switch (hw->mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + adapter->uc_num = hw->mac.num_rar_entries; + adapter->uc_off = 0; + break; + /* multiple ports use mac */ + case MODE_NIC_MODE_2PORT: + case MODE_NIC_MODE_4PORT: + adapter->uc_num = hw->mac.num_rar_entries / 4; + adapter->uc_off = adapter->uc_num * adapter->port; + break; + default: + break; + } + + /* set default ring sizes */ + adapter->tx_ring_item_count = + rnpm_info_tbl[adapter->pf_adapter->board_type]->queue_depth; + adapter->rx_ring_item_count = + rnpm_info_tbl[adapter->pf_adapter->board_type]->queue_depth; + + /* initialize eeprom parameters */ + if (rnpm_init_eeprom_params_generic(hw)) { + e_dev_err("EEPROM initialization failed\n"); + return -EIO; + } + + /*initialization default pause flow */ + hw->fc.requested_mode = rnpm_fc_full; + // hw->fc.requested_mode = rnpm_fc_none; + hw->fc.pause_time = RNPM_DEFAULT_FCPAUSE; + hw->fc.current_mode = rnpm_fc_full; + // hw->fc.current_mode = rnpm_fc_none; + for (i = 0; i < RNPM_MAX_TRAFFIC_CLASS; i++) { + hw->fc.high_water[i] = RNPM_DEFAULT_HIGH_WATER; + hw->fc.low_water[i] = RNPM_DEFAULT_LOW_WATER; + } + + set_bit(__RNPM_DOWN, &adapter->state); + + return 0; +} + +/** + * rnpm_setup_tx_resources - allocate Tx resources (Descriptors) + * @tx_ring: tx descriptor ring (for a specific queue) to setup + * + * Return 0 on success, negative on failure + **/ + +int rnpm_setup_tx_resources(struct rnpm_ring *tx_ring, + struct rnpm_adapter *adapter) +{ + struct device *dev = tx_ring->dev; + int orig_node = dev_to_node(dev); + int numa_node = NUMA_NO_NODE; + int size; + + size = sizeof(struct rnpm_tx_buffer) * tx_ring->count; + + if (tx_ring->q_vector) + numa_node = tx_ring->q_vector->numa_node; + + tx_ring->tx_buffer_info = vzalloc_node(size, numa_node); + if (!tx_ring->tx_buffer_info) + tx_ring->tx_buffer_info = vzalloc(size); + if (!tx_ring->tx_buffer_info) + goto err; + + // memset(tx_ring->tx_buffer_info, 0, size); + + /* round up to nearest 4K */ + tx_ring->size = tx_ring->count * sizeof(struct rnpm_tx_desc); + tx_ring->size = ALIGN(tx_ring->size, 4096); + + set_dev_node(dev, numa_node); + tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, &tx_ring->dma, + GFP_KERNEL); + set_dev_node(dev, orig_node); + if (!tx_ring->desc) + tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, + &tx_ring->dma, GFP_KERNEL); + if (!tx_ring->desc) + goto err; + memset(tx_ring->desc, 0, tx_ring->size); + + tx_ring->next_to_use = 0; + tx_ring->next_to_clean = 0; + + DPRINTK(IFUP, INFO, + "TxRing:%d, vector:%d ItemCounts:%d desc:%p node:%d\n", + tx_ring->rnpm_queue_idx, tx_ring->q_vector->v_idx, + tx_ring->count, tx_ring->desc, numa_node); + + return 0; +err: + rnpm_err( + "%s [SetupTxResources] #%d TxRing:%d, vector:%d ItemCounts:%d\n", + tx_ring->netdev->name, tx_ring->queue_index, + tx_ring->rnpm_queue_idx, tx_ring->q_vector->v_idx, + tx_ring->count); + vfree(tx_ring->tx_buffer_info); + tx_ring->tx_buffer_info = NULL; + dev_err(dev, "Unable to allocate memory for the Tx descriptor ring\n"); + return -ENOMEM; +} + +/** + * rnpm_setup_all_tx_resources - allocate all queues Tx resources + * @adapter: board private structure + * + * If this function returns with an error, then it's possible one or + * more of the rings is populated (while the rest are not). It is the + * callers duty to clean those orphaned rings. + * + * Return 0 on success, negative on failure + **/ +static int rnpm_setup_all_tx_resources(struct rnpm_adapter *adapter) +{ + int i, err = 0; + + tx_dbg("adapter->num_tx_queues:%d, adapter->tx_ring[0]:%p\n", + adapter->num_tx_queues, adapter->tx_ring[0]); + + for (i = 0; i < (adapter->num_tx_queues); i++) { + BUG_ON(adapter->tx_ring[i] == NULL); + err = rnpm_setup_tx_resources(adapter->tx_ring[i], adapter); + if (!err) + continue; + + e_err(probe, "Allocation for Tx Queue %u failed\n", i); + goto err_setup_tx; + } + + return 0; +err_setup_tx: + /* rewind the index freeing the rings as we go */ + while (i--) + rnpm_free_tx_resources(adapter->tx_ring[i]); + return err; +} + +/** + * rnpm_setup_rx_resources - allocate Rx resources (Descriptors) + * @rx_ring: rx descriptor ring (for a specific queue) to setup + * + * Returns 0 on success, negative on failure + **/ +int rnpm_setup_rx_resources(struct rnpm_ring *rx_ring, + struct rnpm_adapter *adapter) +{ + struct device *dev = rx_ring->dev; + int orig_node = dev_to_node(dev); + int numa_node = -1; + int size; + + BUG_ON(rx_ring == NULL); + + size = sizeof(struct rnpm_rx_buffer) * rx_ring->count; + + if (rx_ring->q_vector) + numa_node = rx_ring->q_vector->numa_node; + + rx_ring->rx_buffer_info = vzalloc_node(size, numa_node); + if (!rx_ring->rx_buffer_info) + rx_ring->rx_buffer_info = vzalloc(size); + if (!rx_ring->rx_buffer_info) + goto err; + + // memset(rx_ring->rx_buffer_info, 0, size); + + /* Round up to nearest 4K */ + rx_ring->size = rx_ring->count * sizeof(union rnpm_rx_desc); + rx_ring->size = ALIGN(rx_ring->size, 4096); + + set_dev_node(dev, numa_node); + rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, + GFP_KERNEL); + set_dev_node(dev, orig_node); + if (!rx_ring->desc) + rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, + &rx_ring->dma, GFP_KERNEL); + if (!rx_ring->desc) + goto err; + memset(rx_ring->desc, 0, rx_ring->size); + + rx_ring->next_to_clean = 0; + rx_ring->next_to_use = 0; + + DPRINTK(IFUP, INFO, + "RxRing:%d, vector:%d ItemCounts:%d desc:%p node:%d\n", + rx_ring->rnpm_queue_idx, rx_ring->q_vector->v_idx, + rx_ring->count, rx_ring->desc, numa_node); + + return 0; +err: + rnpm_err( + "%s [SetupRxResources] #%d RxRing:%d, vector:%d ItemCounts:%d error!\n", + rx_ring->netdev->name, rx_ring->queue_index, + rx_ring->rnpm_queue_idx, rx_ring->q_vector->v_idx, + rx_ring->count); + + vfree(rx_ring->rx_buffer_info); + rx_ring->rx_buffer_info = NULL; + dev_err(dev, "Unable to allocate memory for the Rx descriptor ring\n"); + return -ENOMEM; +} + +/** + * rnpm_setup_all_rx_resources - allocate all queues Rx resources + * @adapter: board private structure + * + * If this function returns with an error, then it's possible one or + * more of the rings is populated (while the rest are not). It is the + * callers duty to clean those orphaned rings. + * + * Return 0 on success, negative on failure + **/ +static int rnpm_setup_all_rx_resources(struct rnpm_adapter *adapter) +{ + int i, err = 0; + struct rnpm_hw *hw = &adapter->hw; + u32 head; + + for (i = 0; i < adapter->num_rx_queues; i++) { + BUG_ON(adapter->rx_ring[i] == NULL); + /* should check count and head */ + /* in sriov condition may head large than count */ + head = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD( + adapter->rx_ring[i]->rnpm_queue_idx)); + if (unlikely(head >= adapter->rx_ring[i]->count)) { + dbg("[%s] Ring %d head large than count", + adapter->netdev->name, + adapter->rx_ring[i]->rnpm_queue_idx); + adapter->rx_ring[i]->ring_flags |= + RNPM_RING_FLAG_DELAY_SETUP_RX_LEN; + adapter->rx_ring[i]->reset_count = + adapter->rx_ring[i]->count; + adapter->rx_ring[i]->count = head + 1; + } + err = rnpm_setup_rx_resources(adapter->rx_ring[i], adapter); + if (!err) + continue; + + e_err(probe, "Allocation for Rx Queue %u failed\n", i); + goto err_setup_rx; + } + + return 0; +err_setup_rx: + /* rewind the index freeing the rings as we go */ + while (i--) + rnpm_free_rx_resources(adapter->rx_ring[i]); + return err; +} + +/** + * rnpm_free_tx_resources - Free Tx Resources per Queue + * @tx_ring: Tx descriptor ring for a specific queue + * + * Free all transmit software resources + **/ +void rnpm_free_tx_resources(struct rnpm_ring *tx_ring) +{ + BUG_ON(tx_ring == NULL); + + rnpm_clean_tx_ring(tx_ring); + + vfree(tx_ring->tx_buffer_info); + tx_ring->tx_buffer_info = NULL; + + /* if not set, then don't free */ + if (!tx_ring->desc) + return; + + dma_free_coherent(tx_ring->dev, tx_ring->size, tx_ring->desc, + tx_ring->dma); + + tx_ring->desc = NULL; +} + +/** + * rnpm_free_all_tx_resources - Free Tx Resources for All Queues + * @adapter: board private structure + * + * Free all transmit software resources + **/ +static void rnpm_free_all_tx_resources(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < (adapter->num_tx_queues); i++) + rnpm_free_tx_resources(adapter->tx_ring[i]); +} + +/** + * rnpm_free_rx_resources - Free Rx Resources + * @rx_ring: ring to clean the resources from + * + * Free all receive software resources + **/ +void rnpm_free_rx_resources(struct rnpm_ring *rx_ring) +{ + BUG_ON(rx_ring == NULL); + + rnpm_clean_rx_ring(rx_ring); + + vfree(rx_ring->rx_buffer_info); + rx_ring->rx_buffer_info = NULL; + + /* if not set, then don't free */ + if (!rx_ring->desc) + return; + + dma_free_coherent(rx_ring->dev, rx_ring->size, rx_ring->desc, + rx_ring->dma); + + rx_ring->desc = NULL; +} + +/** + * rnpm_free_all_rx_resources - Free Rx Resources for All Queues + * @adapter: board private structure + * + * Free all receive software resources + **/ +static void rnpm_free_all_rx_resources(struct rnpm_adapter *adapter) +{ + int i; + + for (i = 0; i < (adapter->num_rx_queues); i++) + // if (adapter->rx_ring[i]->desc) + rnpm_free_rx_resources(adapter->rx_ring[i]); +} + +/** + * rnpm_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + **/ +static int rnpm_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + + int max_frame = new_mtu + ETH_HLEN + 2 * ETH_FCS_LEN; + + /* MTU < 68 is an error and causes problems on some kernels */ + if ((new_mtu < RNPM_MIN_MTU) || (max_frame > RNPM_MAX_JUMBO_FRAME_SIZE)) + return -EINVAL; + + e_info(probe, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); + + /* must set new MTU before calling down or up */ + netdev->mtu = new_mtu; + + set_bit(RNPM_PF_SET_MTU, &pf_adapter->flags); + + if (netif_running(netdev)) + rnpm_reinit_locked(adapter); + + return 0; +} + +/** + * rnpm_tx_maxrate - callback to set the maximum per-queue bitrate + * @netdev: network interface device structure + * @queue_index: Tx queue to set + * @maxrate: desired maximum transmit bitrate Mbps + **/ +static int rnpm_tx_maxrate(struct net_device *netdev, int queue_index, + u32 maxrate) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_ring *tx_ring = adapter->tx_ring[queue_index]; + u64 real_rate = 0; + // record this flags + adapter->max_rate[queue_index] = maxrate; + // adapter->flags2 |= RNPM_FLAG2_TX_RATE_SETUP; + if (!maxrate) + return rnpm_setup_tx_maxrate(adapter->hw.hw_addr, tx_ring, 0, + adapter->hw.usecstocount * + 1000000); + /* we need turn it to bytes/s */ + real_rate = (maxrate * 1024 * 1024) / 8; + rnpm_setup_tx_maxrate(adapter->hw.hw_addr, tx_ring, real_rate, + adapter->hw.usecstocount * 1000000); + + return 0; +} +/** + * rnpm_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + **/ +int rnpm_open(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw; + int err; + unsigned long flags; + + DPRINTK(IFUP, INFO, "ifup\n"); + rnpm_logd(LOG_FUNC_ENTER, "enter %s %s\n", __func__, netdev->name); + + // rnpm_mbx_ifup_down(&adapter->hw, 1); + + /* disallow open during test */ + if (test_bit(__RNPM_TESTING, &adapter->state)) + return -EBUSY; + hw = &adapter->hw; + netif_carrier_off(netdev); + /* allocate transmit descriptors */ + err = rnpm_setup_all_tx_resources(adapter); + if (err) + goto err_setup_tx; + /* allocate receive descriptors */ + err = rnpm_setup_all_rx_resources(adapter); + if (err) + goto err_setup_rx; + rnpm_configure(adapter); + err = rnpm_request_irq(adapter); + if (err) + goto err_req_irq; + /* Notify the stack of the actual queue counts. */ + err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues); + if (err) + goto err_set_queues; + err = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues); + if (err) + goto err_set_queues; +#ifndef NO_PTP + if (module_enable_ptp) + rnpm_ptp_register(adapter); +#endif + rnpm_up_complete(adapter); + + /* set sw dummy 0, wait fw link to force one interrupt */ + rnpm_link_stat_mark(hw, hw->nr_lane, 0); + spin_lock_irqsave(&adapter->pf_adapter->pf_setup_lock, flags); + // set_bit(RNPM_PF_SERVICE_SKIP_HANDLE, &adapter->pf_adapter->flags); + set_bit(RNPM_PF_LINK_CHANGE, &adapter->pf_adapter->flags); + spin_unlock_irqrestore(&adapter->pf_adapter->pf_setup_lock, flags); + rnpm_logd(LOG_FUNC_ENTER, "exit %s %s\n", __func__, + adapter->netdev->name); + + return 0; + +err_set_queues: + rnpm_free_irq(adapter); +err_req_irq: + rnpm_free_all_rx_resources(adapter); +err_setup_rx: + rnpm_free_all_tx_resources(adapter); +err_setup_tx: + rnpm_mbx_ifup_down(&adapter->hw, MBX_IFDOWN); + rnpm_reset(adapter); + e_err(drv, "open failed!\n"); + + return err; +} + +/** + * rnpm_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ +int rnpm_close(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + + DPRINTK(IFDOWN, INFO, "ifdown\n"); + rnpm_logd(LOG_FUNC_ENTER, "enter %s %s\n", __func__, + adapter->netdev->name); + + /* should clean adapter->ptp_tx_skb */ + if (adapter->ptp_tx_skb) { + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + adapter->tx_hwtstamp_timeouts++; + netdev_warn(adapter->netdev, "clearing Tx timestamp hang\n"); + } + +#ifndef NO_PTP + if (module_enable_ptp) + rnpm_ptp_unregister(adapter); +#endif + + rnpm_down(adapter); + rnpm_free_irq(adapter); + rnpm_fdir_filter_exit(adapter); + rnpm_free_all_tx_resources(adapter); + rnpm_free_all_rx_resources(adapter); + rnpm_mbx_ifup_down(&adapter->hw, MBX_IFDOWN); + // rnpm_release_hw_control(adapter); + /* when down, disable fw link event interrupt */ + rnpm_link_stat_mark(&adapter->hw, adapter->hw.nr_lane, 0); + spin_lock_irqsave(&adapter->pf_adapter->pf_setup_lock, flags); + set_bit(RNPM_PF_SERVICE_SKIP_HANDLE, &adapter->pf_adapter->flags); + set_bit(RNPM_PF_LINK_CHANGE, &adapter->pf_adapter->flags); + spin_unlock_irqrestore(&adapter->pf_adapter->pf_setup_lock, flags); + // adapter->hw.mac.ops.clear_hw_cntrs(&adapter->hw); + rnpm_logd(LOG_FUNC_ENTER, "exit %s %s\n", __func__, + adapter->netdev->name); + + return 0; +} + +/** + * rnpm_update_stats - Update the board statistics counters. + * @adapter: board private structure + **/ +void rnpm_update_stats(struct rnpm_adapter *adapter) +{ + struct net_device_stats *net_stats = &adapter->netdev->stats; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_hw_stats *hw_stats = &adapter->hw_stats; + + int i, port = adapter->port; + struct rnpm_ring *ring; + u64 hw_csum_rx_error = 0; + u64 hw_csum_rx_good = 0; + u64 rx_crc_error = 0; + + net_stats->tx_packets = 0; + net_stats->tx_bytes = 0; + net_stats->rx_packets = 0; + net_stats->rx_bytes = 0; + + hw_stats->vlan_strip_cnt = 0; + hw_stats->vlan_add_cnt = 0; + + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return; + + for (i = 0; i < adapter->num_q_vectors; i++) { + rnpm_for_each_ring(ring, adapter->q_vector[i]->rx) { + net_stats->rx_packets += ring->stats.packets; + net_stats->rx_bytes += ring->stats.bytes; + hw_csum_rx_error += ring->rx_stats.csum_err; + hw_csum_rx_good += ring->rx_stats.csum_good; + hw_stats->vlan_strip_cnt += ring->rx_stats.vlan_remove; + } + rnpm_for_each_ring(ring, adapter->q_vector[i]->tx) { + net_stats->tx_packets += ring->stats.packets; + net_stats->tx_bytes += ring->stats.bytes; + hw_stats->vlan_add_cnt += ring->tx_stats.vlan_add; + } + } + + switch (hw->mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + hw_stats->dma_to_eth = + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_0) + + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_1) + + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_2) + + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_3); + break; + case MODE_NIC_MODE_2PORT: + hw_stats->dma_to_eth = 0; + for (i = port * 2; i < (port + 1) * 2; i++) { + hw_stats->dma_to_eth += + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL(i)); + } + break; + case MODE_NIC_MODE_4PORT: + hw_stats->dma_to_eth = + rd32(hw, RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL(port)); + break; + } + + /* only has unique reg */ + hw_stats->dma_to_switch = rd32(hw, RNPM_DMA_STATS_DMA_TO_SWITCH); + hw_stats->mac_to_dma = rd32(hw, RNPM_DMA_STATS_MAC_TO_DMA); + + rx_crc_error = rnpm_recalculate_err_pkts( + rd32(hw, RNPM_RXTRANS_CRC_ERR_PKTS(port)), + &(hw->err_pkts_init.crc[port]), false); + net_stats->rx_crc_errors = rx_crc_error; + // hw->err_pkts_init.scsum[port] = hw_csum_rx_error; + + net_stats->rx_errors += + rnpm_recalculate_err_pkts(rd32(hw, + RNPM_RXTRANS_WDT_ERR_PKTS(port)), + &hw->err_pkts_init.wdt[port], false) + + rnpm_recalculate_err_pkts( + rd32(hw, RNPM_RXTRANS_CODE_ERR_PKTS(port)), + &hw->err_pkts_init.code[port], false) + + rnpm_recalculate_err_pkts( + rd32(hw, RNPM_RXTRANS_SLEN_ERR_PKTS(port)), + &hw->err_pkts_init.slen[port], false) + + rnpm_recalculate_err_pkts( + rd32(hw, RNPM_RXTRANS_GLEN_ERR_PKTS(port)), + &hw->err_pkts_init.glen[port], false) + + rnpm_recalculate_err_pkts(rd32(hw, + RNPM_RXTRANS_IPH_ERR_PKTS(port)), + &hw->err_pkts_init.iph[port], false) + + rnpm_recalculate_err_pkts(rd32(hw, + RNPM_RXTRANS_LEN_ERR_PKTS(port)), + &hw->err_pkts_init.len[port], false) + + rnpm_recalculate_err_pkts( + rd32(hw, RNPM_RXTRANS_CSUM_ERR_PKTS(port)), + &hw->err_pkts_init.csum[port], false) + + rnpm_recalculate_err_pkts(hw_csum_rx_error, + &hw->err_pkts_init.scsum[port], + true) + + rx_crc_error; + net_stats->rx_dropped += + rnpm_recalculate_err_pkts(rd32(hw, + RNPM_RXTRANS_CUT_ERR_PKTS(port)), + &hw->err_pkts_init.cut[port], false) + + rnpm_recalculate_err_pkts(rd32(hw, + RNPM_RXTRANS_DROP_PKTS(port)), + &hw->err_pkts_init.drop[port], false); + adapter->hw_csum_rx_error = hw_csum_rx_error; + adapter->hw_csum_rx_good = hw_csum_rx_good; + + hw_stats->mac_rx_broadcast = + rd32(hw, RNPM_MAC_STATS_BROADCAST_LOW(port)); + hw_stats->mac_rx_broadcast += + ((u64)rd32(hw, RNPM_MAC_STATS_BROADCAST_HIGH(port)) << 32); + + // maybe no use + hw_stats->mac_rx_multicast = + rd32(hw, RNPM_MAC_STATS_MULTICAST_LOW(port)); + hw_stats->mac_rx_multicast += + ((u64)rd32(hw, RNPM_MAC_STATS_MULTICAST_HIGH(port)) << 32); + + /* store to net_stats */ + net_stats->multicast = hw_stats->mac_rx_multicast; + hw_stats->mac_tx_pause_cnt = + rd32(hw, RNPM_MAC_STATS_TX_PAUSE_LOW(port)); + hw_stats->mac_tx_pause_cnt += + ((u64)rd32(hw, RNPM_MAC_STATS_TX_PAUSE_HIGH(port)) << 32); + + hw_stats->mac_rx_pause_cnt = + rd32(hw, RNPM_MAC_STATS_RX_PAUSE_LOW(port)); + hw_stats->mac_rx_pause_cnt += + ((u64)rd32(hw, RNPM_MAC_STATS_RX_PAUSE_HIGH(port)) << 32); +} + +/** + * rnpm_check_hang_subtask - check for hung queues and dropped interrupts + * @adapter: pointer to the device adapter structure + * + * This function serves two purposes. First it strobes the interrupt lines + * in order to make certain interrupts are occurring. Secondly it sets the + * bits needed to check for TX hangs. As a result we should immediately + * determine if a hang has occurred. + */ +static void rnpm_check_hang_subtask(struct rnpm_adapter *adapter) +{ + // struct rnpm_hw *hw = &adapter->hw; + // u64 eics = 0; + int i; + struct rnpm_ring *tx_ring; + u64 tx_next_to_clean_old; + u64 tx_next_to_clean; + u64 tx_next_to_use; + struct rnpm_ring *rx_ring; + u64 rx_next_to_clean_old; + u64 rx_next_to_clean; + union rnpm_rx_desc *rx_desc; + int size; + struct rnpm_q_vector *q_vector; + + /* If we're down or resetting, just bail */ + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return; + + /* Force detection of hung controller */ + if (netif_carrier_ok(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) + set_check_for_tx_hang(adapter->tx_ring[i]); + } + + for (i = 0; i < adapter->num_tx_queues; i++) { + tx_ring = adapter->tx_ring[i]; + /* get the last next_to_clean */ + tx_next_to_clean_old = tx_ring->tx_stats.tx_next_to_clean; + tx_next_to_clean = tx_ring->next_to_clean; + tx_next_to_use = tx_ring->next_to_use; + + /* if we have tx desc to clean */ + if (tx_next_to_use != tx_next_to_clean) { + if (tx_next_to_clean == tx_next_to_clean_old) { + tx_ring->tx_stats.tx_equal_count++; + if (tx_ring->tx_stats.tx_equal_count > 2) { + /* maybe not so good */ + struct rnpm_q_vector *q_vector = + tx_ring->q_vector; + + /* stats */ + if (q_vector->rx.ring || + q_vector->tx.ring) { + rnpm_irq_disable_queues( + q_vector); + napi_schedule_irqoff( + &q_vector->napi); + } + + tx_ring->tx_stats.tx_irq_miss++; + tx_ring->tx_stats.tx_equal_count = 0; + } + } else { + tx_ring->tx_stats.tx_equal_count = 0; + } + /* update */ + /* record this next_to_clean */ + tx_ring->tx_stats.tx_next_to_clean = tx_next_to_clean; + } else { + /* clean record to -1 */ + tx_ring->tx_stats.tx_next_to_clean = -1; + } + } + + // check if we lost rx irq + for (i = 0; i < adapter->num_rx_queues; i++) { + rx_ring = adapter->rx_ring[i]; + /* get the last next_to_clean */ + rx_next_to_clean_old = rx_ring->rx_stats.rx_next_to_clean; + /* get the now clean */ + rx_next_to_clean = rx_ring->next_to_clean; + + // if rx clean stopped + // maybe not so good + if (rx_next_to_clean == rx_next_to_clean_old) { + rx_ring->rx_stats.rx_equal_count++; + + if ((rx_ring->rx_stats.rx_equal_count > 2) && + (rx_ring->rx_stats.rx_equal_count < 5)) { + // check if dd in the clean rx desc + rx_desc = RNPM_RX_DESC(rx_ring, + rx_ring->next_to_clean); + + if (!rnpm_test_staterr(rx_desc, + RNPM_RXD_STAT_DD)) + goto skip; + + q_vector = rx_ring->q_vector; + size = le16_to_cpu(rx_desc->wb.len); + if (!size) + goto skip; + rx_ring->rx_stats.rx_irq_miss++; + if (q_vector->rx.ring || q_vector->tx.ring) { + rnpm_irq_disable_queues(q_vector); + napi_schedule_irqoff(&q_vector->napi); + } + } +skip: + if (rx_ring->rx_stats.rx_equal_count > 1000) + rx_ring->rx_stats.rx_equal_count = 0; + } else { + rx_ring->rx_stats.rx_equal_count = 0; + } + // update new clean + rx_ring->rx_stats.rx_next_to_clean = rx_next_to_clean; + } +} + +static int rnpm_pf_get_port_link_stat(struct rnpm_pf_adapter *pf_adapter) +{ + struct rnpm_hw *hw; + int err = 0, i; + + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + if (rnpm_port_is_valid(pf_adapter, i)) { + if (pf_adapter->adapter[i]) { + hw = &pf_adapter->adapter[i]->hw; + if (rnpm_mbx_get_lane_stat(hw) < 0) + goto error; + // hw->link ? rnpm_link_stat_mark(hw, hw->nr_lane, 1) + // : rnpm_link_stat_mark(hw, hw->nr_lane, 0); + + if (hw->phy_type == PHY_TYPE_SGMII) { + /* get an */ + err = rnpm_mbx_phy_read( + hw, 0, &hw->phy.vb_r[0]); + if (err) + goto error; + hw->phy.an = + (hw->phy.vb_r[0] & BIT(12)) ? + AUTONEG_ENABLE : + AUTONEG_DISABLE; + err = rnpm_mbx_phy_read( + hw, 4, &hw->phy.vb_r[4]); + if (err) + goto error; + err = rnpm_mbx_phy_read( + hw, 9, &hw->phy.vb_r[9]); + if (err) + goto error; + err = rnpm_mbx_phy_read( + hw, 17, &hw->phy.vb_r[17]); + if (err) + goto error; + hw->phy.is_mdix = + !!(hw->phy.vb_r[17] & 0x0040); + } + } + } + } + +error: + return err; +} + +/** + * rnpm_watchdog_update_link - update the link status + * @adapter: pointer to the device adapter structure + * @link_speed: pointer to a u32 to store the link_speed + **/ +static int rnpm_watchdog_update_link(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 link_speed = RNPM_LINK_SPEED_UNKNOWN; + bool link_up = false; + + if (!(adapter->flags & RNPM_FLAG_NEED_LINK_UPDATE)) + return 1; + + /* Need update port link state */ + if (rnpm_pf_get_port_link_stat(adapter->pf_adapter) < 0) { + set_bit(RNPM_PF_LINK_CHANGE, &adapter->pf_adapter->flags); + return 1; + } + + if (hw->mac.ops.check_link) { + hw->mac.ops.check_link(hw, &link_speed, &link_up, false); + } else { + /* always assume link is up, if no check link function */ + link_speed = RNPM_LINK_SPEED_10GB_FULL; + link_up = true; + rnpm_logd(LOG_LINK_EVENT, + "WARN: %s:%s: check_link is null, force speed/link\n", + __func__, adapter->netdev->name); + } + + if (link_up || time_after(jiffies, (adapter->link_check_timeout + + RNPM_TRY_LINK_TIMEOUT))) { + adapter->flags &= ~RNPM_FLAG_NEED_LINK_UPDATE; + } + adapter->link_up = link_up; + adapter->link_speed = link_speed; + + return 0; +} + +static void rnpm_update_default_up(struct rnpm_adapter *adapter) +{ +#ifdef CONFIG_RNPM_DCB + struct net_device *netdev = adapter->netdev; + struct dcb_app app = { + .selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE, + .protocol = 0, + }; + u8 up = 0; + + if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) + up = dcb_ieee_getapp_mask(netdev, &app); + + adapter->default_up = (up > 1) ? (ffs(up) - 1) : 0; +#endif +} + +/** + * rnpm_watchdog_link_is_up - update netif_carrier status and + * print link up message + * @adapter: pointer to the device adapter structure + **/ +static void rnpm_watchdog_link_is_up(struct rnpm_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct rnpm_hw *hw = &adapter->hw; + u32 link_speed = adapter->link_speed; + bool flow_rx = false, flow_tx = false; + + rnpm_link_stat_mark(hw, hw->nr_lane, 1); + + /* only continue if link was previously down */ + if (netif_carrier_ok(netdev)) + return; + + hw->mac.ops.fc_enable(hw); + + adapter->flags2 &= ~RNPM_FLAG2_SEARCH_FOR_SFP; + + if (hw->fc.current_mode == rnpm_fc_rx_pause) { + flow_rx = true; + } else if (hw->fc.current_mode == rnpm_fc_tx_pause) { + flow_tx = true; + } else if (hw->fc.current_mode == rnpm_fc_full) { + flow_rx = true; + flow_tx = true; + } + + e_info(drv, "NIC Link is Up %s, Flow Control: %s\n", + (link_speed == RNPM_LINK_SPEED_10GB_FULL ? + "10 Gbps" : + (link_speed == RNPM_LINK_SPEED_1GB_FULL ? + "1 Gbps" : + (link_speed == RNPM_LINK_SPEED_100_FULL ? + "100 Mbps" : + (link_speed == RNPM_LINK_SPEED_10_FULL ? + "10 Mbps" : + "unknown speed")))), + ((flow_rx && flow_tx) ? + "RX/TX" : + (flow_rx ? "RX" : (flow_tx ? "TX" : "None")))); + + netif_carrier_on(netdev); + + netif_tx_wake_all_queues(netdev); + // rnpm_check_vf_rate_limit(adapter); + + /* update the default user priority for VFs */ + rnpm_update_default_up(adapter); + control_mac_rx(adapter, true); + /* ping all the active vfs to let them know link has changed */ + // rnpm_ping_all_vfs(adapter); +} + +/** + * rnpm_watchdog_link_is_down - update netif_carrier status and + * print link down message + * @adapter: pointer to the adapter structure + **/ +static void rnpm_watchdog_link_is_down(struct rnpm_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct rnpm_hw *hw = &adapter->hw; + + adapter->link_up = false; + adapter->link_speed = 0; + + rnpm_link_stat_mark(hw, hw->nr_lane, 0); + + /* only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + + control_mac_rx(adapter, false); + + /* poll for SFP+ cable when link is down */ + if (rnpm_is_sfp(hw)) + adapter->flags2 |= RNPM_FLAG2_SEARCH_FOR_SFP; + + e_info(drv, "NIC Link is Down\n"); + netif_carrier_off(netdev); + + netif_tx_stop_all_queues(netdev); + /* ping all the active vfs to let them know link has changed */ + // rnpm_ping_all_vfs(adapter); +} + +/** + * rnpm_watchdog_flush_tx - flush queues on link down + * @adapter: pointer to the device adapter structure + **/ +__maybe_unused static void rnpm_watchdog_flush_tx(struct rnpm_adapter *adapter) +{ + int i; + int some_tx_pending = 0; + + if (!netif_carrier_ok(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) { + struct rnpm_ring *tx_ring = adapter->tx_ring[i]; + + if (tx_ring->next_to_use != tx_ring->next_to_clean) { + some_tx_pending = 1; + break; + } + } + + if (some_tx_pending) { + /* We've lost link, so the controller stops DMA, + * but we've got queued Tx work that's never going + * to get done, so reset controller to flush Tx. + * (Do the reset outside of interrupt context). + */ + rnpm_dbg( + "initiating reset to clear Tx work after link loss\n"); + e_warn(drv, + "initiating reset to clear Tx work after link loss\n"); + // adapter->flags2 |= RNPM_FLAG2_RESET_REQUESTED; + set_bit(RNPM_PF_RESET, &adapter->pf_adapter->flags); + } + } +} + +/** + * rnpm_watchdog_subtask - check and bring link up + * @adapter: pointer to the device adapter structure + **/ +static void rnpm_watchdog_subtask(struct rnpm_adapter *adapter) +{ + // rnpm_logd(LOG_FUNC_ENTER, + // "enter %s %s state=0x%lx\n", + // __func__, + // adapter->netdev->name, + // adapter->state); + /* if interface is down do nothing */ + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return; + + rnpm_update_stats(adapter); + if (rnpm_watchdog_update_link(adapter)) + return; + + if ((adapter->link_up)) + rnpm_watchdog_link_is_up(adapter); + else + rnpm_watchdog_link_is_down(adapter); + + // rnpm_watchdog_flush_tx(adapter); + // rnpm_logd(LOG_FUNC_ENTER, + // "exit %s %s state=0x%lx\n", + // __func__, + // adapter->netdev->name, + // adapter->state); +} + +/** + * rnpm_sfp_detection_subtask - poll for SFP+ cable + * @adapter: the rnpm adapter structure + **/ +static void rnpm_sfp_detection_subtask(struct rnpm_adapter *adapter) +{ +} + +/** + * rnpm_sfp_link_config_subtask - set up link SFP after module install + * @adapter: the rnpm adapter structure + **/ +static void rnpm_sfp_link_config_subtask(struct rnpm_adapter *adapter) +{ + // struct rnpm_hw *hw = &adapter->hw; + // u32 speed; + // bool autoneg = false; + + if (!(adapter->flags & RNPM_FLAG_NEED_LINK_CONFIG)) + return; + +} + +#ifdef CONFIG_PCI_IOV +__maybe_unused static void rnpm_check_for_bad_vf(struct rnpm_adapter *adapter) +{ +} + +#endif +/** + * rnpm_pf_service_timer - Timer Call-back + * @data: pointer to adapter cast into an unsigned long + **/ +void rnpm_pf_service_timer(struct timer_list *t) +{ + struct rnpm_pf_adapter *pf_adapter = + from_timer(pf_adapter, t, service_timer); + unsigned long next_event_offset; + + // we check 2s + next_event_offset = HZ * 2; + pf_adapter->timer_count++; + /* Reset the timer */ + mod_timer(&pf_adapter->service_timer, next_event_offset + jiffies); + rnpm_pf_service_event_schedule(pf_adapter); +} + +/** + * rnpm_service_timer - Timer Call-back + * @data: pointer to adapter cast into an unsigned long + **/ +void rnpm_service_timer(struct timer_list *t) +{ + struct rnpm_adapter *adapter = from_timer(adapter, t, service_timer); + unsigned long next_event_offset; + bool ready = true; + + /* poll faster when waiting for link */ + if (adapter->flags & RNPM_FLAG_NEED_LINK_UPDATE) + next_event_offset = HZ / 10; + else + next_event_offset = HZ * 2; + adapter->timer_count++; + /* Reset the timer */ + mod_timer(&adapter->service_timer, next_event_offset + jiffies); + + if (ready) + rnpm_service_event_schedule(adapter); +} + +static void rnpm_fix_dma_tx_status(struct rnpm_pf_adapter *pf_adapter) +{ + int i; + + // set all tx start to 1 + for (i = 0; i < 128; i++) + wr32(pf_adapter, RNPM_DMA_TX_START(i), 1); +} + +static int rnpm_reset_pf(struct rnpm_pf_adapter *pf_adapter) +{ + int times = 0; + int i = 0; + u32 status = 0; +#ifdef NO_MBX_VERSION + unsigned long flags; +#endif + + wr32(pf_adapter, RNPM_DMA_AXI_EN, 0); +#define TIMEOUT_COUNT (1000) + /* wait axi ready */ + while ((status != 0xf) && (times < TIMEOUT_COUNT)) { + status = rd32(pf_adapter, RNPM_DMA_AXI_STAT); + usleep_range(4000, 8000); + times++; + // rnpm_dbg("wait axi ready\n"); + } + if (!pf_adapter->hw.ncsi_en) { + if (times >= TIMEOUT_COUNT) { + rnpm_warn("wait axi ready timeout\n"); + return -1; + } + } + + wr32(pf_adapter, RNPM_TOP_NIC_REST_N, NIC_RESET); + /* we need this */ + wmb(); + + wr32(pf_adapter, RNPM_TOP_NIC_REST_N, ~NIC_RESET); + +#ifdef NO_MBX_VERSION +#define TSRN10_REG_DEBUG_VALUE (0x1a2b3c4d) + + spin_lock_irqsave(&pf_adapter->dummy_setup_lock, flags); + wr32(pf_adapter, RNPM_DMA_DUMY, TSRN10_REG_DEBUG_VALUE); + times = 0; + status = 0; + while ((status != TSRN10_REG_DEBUG_VALUE + 1) && + (times < TIMEOUT_COUNT)) { + status = rd32(pf_adapter, RNPM_DMA_DUMY); + times++; + usleep_range(4000, 8000); + // rnpm_dbg("wait firmware reset card %x\n", status); + } + spin_unlock_irqrestore(&pf_adapter->dummy_setup_lock, flags); + + if (times >= TIMEOUT_COUNT) { + rnpm_dbg("wait firmware reset card timeout\n"); + return -ETIME; + } +#else + rnpm_mbx_fw_reset_phy(&pf_adapter->hw); +#endif + + /* global setup here */ + wr32(pf_adapter, RNPM_TOP_ETH_BUG_40G_PATCH, 1); + wr32(pf_adapter, RNPM_ETH_TUNNEL_MOD, 1); + + /* set all rx drop */ + for (i = 0; i < 4; i++) + wr32(pf_adapter, RNPM_ETH_RX_PROGFULL_THRESH_PORT(i), + DROP_ALL_THRESH); + + // rnpm_dbg("reset_finish\n"); + /* setup rss key */ + rnpm_init_rss_key(pf_adapter); + /* tcam setup */ + if (pf_adapter->adapter_cnt == 1) { + wr32(pf_adapter, RNPM_ETH_TCAM_EN, 1); + wr32(pf_adapter, RNPM_TOP_ETH_TCAM_CONFIG_ENABLE, 1); + wr32(pf_adapter, RNPM_TCAM_MODE, 2); +#define TCAM_NUM (4096) + for (i = 0; i < TCAM_NUM; i++) { + wr32(pf_adapter, RNPM_TCAM_SDPQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_DAQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_SAQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_APQF(i), 0); + + wr32(pf_adapter, RNPM_TCAM_SDPQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_DAQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_SAQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_APQF_MASK(i), 0); + } + wr32(pf_adapter, RNPM_TCAM_MODE, 1); + } + // should open all tx + rnpm_fix_dma_tx_status(pf_adapter); +#define DEFAULT_MIN_SIZE 60 +#define DEFAULT_MAX_SIZE 1522 + wr32(pf_adapter, RNPM_ETH_DEFAULT_RX_MIN_LEN, DEFAULT_MIN_SIZE); + wr32(pf_adapter, RNPM_ETH_DEFAULT_RX_MAX_LEN, DEFAULT_MAX_SIZE); + // wr32(pf_adapter, RNPM_ETH_ERR_MASK_VECTOR, ETH_ERR_PKT_LEN_ERR | + // ETH_ERR_HDR_LEN_ERR); + + switch (pf_adapter->hw.mode) { + case MODE_NIC_MODE_1PORT: + case MODE_NIC_MODE_4PORT: + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(0), 0); + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(1), 1); + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(2), 2); + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(3), 3); + + break; + case MODE_NIC_MODE_2PORT: + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(0), 0); + wr32(pf_adapter, RNPM_ETH_TC_PORT_OFFSET_TABLE(1), 2); + break; + } + + return 0; +} + +__maybe_unused void wait_all_port_resetting(struct rnpm_pf_adapter *pf_adapter) +{ + int i; + struct rnpm_adapter *adapter; + // should wait all + for (i = 0; i < pf_adapter->adapter_cnt - 1; i++) { + adapter = pf_adapter->adapter[i]; + while (test_and_set_bit(__RNPM_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + } +} + +__maybe_unused void clean_all_port_resetting(struct rnpm_pf_adapter *pf_adapter) +{ + int i; + struct rnpm_adapter *adapter; + // should wait all + for (i = 0; i < pf_adapter->adapter_cnt - 1; i++) { + adapter = pf_adapter->adapter[i]; + clear_bit(__RNPM_RESETTING, &adapter->state); + } +} + +static void rnpm_pf_mtu_subtask(struct rnpm_pf_adapter *pf_adapter) +{ + int i; + struct rnpm_adapter *adapter; + struct net_device *netdev; + int mtu = 0; + + for (i = pf_adapter->adapter_cnt - 1; i >= 0; i--) { + adapter = pf_adapter->adapter[i]; + if (adapter) { + netdev = adapter->netdev; + + if (mtu < netdev->mtu) + mtu = netdev->mtu; + } + } + mtu = mtu + ETH_HLEN + 2 * ETH_FCS_LEN; + + wr32(pf_adapter, RNPM_ETH_DEFAULT_RX_MAX_LEN, mtu); +} + +static void rnpm_pf_reset_subtask(struct rnpm_pf_adapter *pf_adapter) +{ + int err = 0; + int i; + struct rnpm_adapter *adapter; + struct net_device *netdev; + + while (test_and_set_bit(__RNPM_RESETTING, &pf_adapter->state)) { + if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) { + clear_bit(__RNPM_RESETTING, &pf_adapter->state); + return; + } + usleep_range(1000, 2000); + } + rnpm_warn("rx/tx hang detected, reset pf\n"); + // try to pf nic reset + err = rnpm_reset_pf(pf_adapter); + + // first stop all port + for (i = pf_adapter->adapter_cnt - 1; i >= 0; i--) { + adapter = pf_adapter->adapter[i]; + if (!adapter) + continue; + + netdev = adapter->netdev; + rtnl_lock(); + netif_device_detach(netdev); + if (netif_running(netdev)) { + rnpm_down(adapter); + rnpm_free_irq(adapter); + rnpm_free_all_tx_resources(adapter); + rnpm_free_all_rx_resources(adapter); + rnpm_mbx_ifup_down(&adapter->hw, MBX_IFDOWN); + } + /* free msix */ + // adapter->rm_mode = true; + rnpm_clear_interrupt_scheme(adapter); + rtnl_unlock(); + } + + // set all port up + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + adapter = pf_adapter->adapter[i]; + if (!adapter) + continue; + + netdev = adapter->netdev; + // rnpm_reset(adapter); + rtnl_lock(); + err = rnpm_init_interrupt_scheme(adapter); + if (!err && netif_running(netdev)) + err = rnpm_open(netdev); + + netif_device_attach(netdev); + rtnl_unlock(); + } + + clear_bit(__RNPM_RESETTING, &pf_adapter->state); +} + +static void rnpm_reset_subtask(struct rnpm_adapter *adapter) +{ + if (!(adapter->flags2 & RNPM_FLAG2_RESET_REQUESTED)) + return; + + adapter->flags2 &= ~RNPM_FLAG2_RESET_REQUESTED; + + /* If we're already down or resetting, just bail */ + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return; + + // rnpm_dump(adapter); + netdev_err(adapter->netdev, "Reset adapter\n"); + adapter->tx_timeout_count++; + + rnpm_reinit_locked(adapter); +} + +static void rnpm_rx_len_reset_subtask(struct rnpm_adapter *adapter) +{ + int i; + struct rnpm_ring *rx_ring; + // struct net_device *netdev = adapter->netdev; + + for (i = 0; i < adapter->num_tx_queues; i++) { + rx_ring = adapter->rx_ring[i]; + if (unlikely(rx_ring->ring_flags & + RNPM_RING_FLAG_DO_RESET_RX_LEN)) { + dbg("[%s] Rx-ring %d count reset\n", + adapter->netdev->name, rx_ring->rnpm_queue_idx); + rnpm_rx_ring_reinit(adapter, rx_ring); + rx_ring->ring_flags &= + (~RNPM_RING_FLAG_DO_RESET_RX_LEN); + } + } +} + +/* just modify rx itr */ +// static void rnpm_auto_itr_moderation(struct rnpm_adapter *adapter) +//{ +// int i; +// struct rnpm_ring *rx_ring; +// u64 period = (u64)(jiffies - adapter->last_moder_jiffies); +// u32 pkt_rate_high, pkt_rate_low; +// struct rnpm_hw *hw = &adapter->hw; +// u64 packets; +// u64 rate; +// u64 avg_pkt_size; +// u64 rx_packets; +// u64 rx_bytes; +// u64 rx_pkt_diff; +// u32 itr_reg; +// int moder_time; +// +// /* if interface is down do nothing */ +// if (test_bit(__RNPM_DOWN, &adapter->state) || +// test_bit(__RNPM_RESETTING, &adapter->state)) +// return; +// +// if (!adapter->auto_rx_coal) +// return; +// +// if (!adapter->adaptive_rx_coal || period < adapter->sample_interval * HZ) { +// return; +// } +// pkt_rate_low = READ_ONCE(adapter->pkt_rate_low); +// pkt_rate_high = READ_ONCE(adapter->pkt_rate_high); +// +// /* it is time to check moderation */ +// for (i = 0; i < adapter->num_rx_queues; i++) { +// rx_ring = adapter->rx_ring[i]; +// rx_packets = READ_ONCE(rx_ring->stats.packets); +// rx_bytes = READ_ONCE(rx_ring->stats.bytes); +// rx_pkt_diff = +// rx_packets - adapter->last_moder_packets[rx_ring->queue_index]; +// packets = rx_pkt_diff; +// rate = packets * HZ / period; +// +// avg_pkt_size = +// packets +// ? (rx_bytes - adapter->last_moder_bytes[rx_ring->queue_index]) / +// packets +// : 0; +// +// if (rate > (RNPM_RX_RATE_THRESH / adapter->num_rx_queues) && +// avg_pkt_size > RNPM_AVG_PKT_SMALL) { +// if (rate <= pkt_rate_low) +// moder_time = adapter->rx_usecs_low; +// else if (rate >= pkt_rate_high) +// moder_time = adapter->rx_usecs_high; +// else +// moder_time = +// (rate - pkt_rate_low) * +// (adapter->rx_usecs_high - adapter->rx_usecs_low) / +// (pkt_rate_high - pkt_rate_low) + +// adapter->rx_usecs_low; +// } else { +// moder_time = adapter->rx_usecs_low; +// } +// +// if (moder_time != adapter->last_moder_time[rx_ring->queue_index]) { +// #ifdef UV3P_1PF +// itr_reg = moder_time * 300; // 150M +// #else +// itr_reg = moder_time * 125; // 62.5M +// #endif +// /* setup time to hw */ +// wr32(hw, +// RNPM_DMA_REG_RX_INT_DELAY_TIMER(rx_ring->rnpm_queue_idx), +// itr_reg); +// adapter->last_moder_time[rx_ring->queue_index] = moder_time; +// } +// /* write back new count */ +// adapter->last_moder_packets[rx_ring->queue_index] = rx_packets; +// adapter->last_moder_bytes[rx_ring->queue_index] = rx_bytes; +// } +// } +// todo check lock status ? +int rnpm_check_mc_addr(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + u32 mta_shadow[RNPM_MAX_MTA]; + int i; + int j; + int ret = 0; + struct rnpm_hw *hw; + /* store old data */ + memcpy(mta_shadow, pf_adapter->mta_shadow, sizeof(u32) * RNPM_MAX_MTA); + /* calculate new data */ + for (i = 0; i < RNPM_MAX_MTA; i++) { + pf_adapter->mta_shadow[i] = 0; + for (j = 0; j < pf_adapter->adapter_cnt; j++) { + if (rnpm_port_is_valid(pf_adapter, j)) { + hw = &pf_adapter->adapter[j]->hw; + pf_adapter->mta_shadow[i] |= + hw->mac.mta_shadow[j]; + } + } + if (pf_adapter->mta_shadow[i] != mta_shadow[i]) + ret = 1; + } + return ret; +} + +void update_pf_vlan(struct rnpm_adapter *adapter) +{ +} + +__maybe_unused static void +rnpm_update_feature_subtask(struct rnpm_adapter *adapter) +{ + struct rnpm_pf_adapter __maybe_unused *pf_adapter = adapter->pf_adapter; + u32 changed = 0; + netdev_features_t features = adapter->netdev->features; + /* if interface is down do nothing */ + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return; + + /* update vlan filter status maybe other port update the unique status */ + if (adapter->flags_feature & RNPM_FLAG_DELAY_UPDATE_VLAN_FILTER) { + if (pf_adapter->vlan_status_true) { + if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER)) { + features |= NETIF_F_HW_VLAN_CTAG_FILTER; + changed = 1; + } + } else { + if (features & NETIF_F_HW_VLAN_CTAG_FILTER) { + features &= (~NETIF_F_HW_VLAN_CTAG_FILTER); + changed = 1; + } + } + } + if (changed) + adapter->netdev->features = features; + if (adapter->flags_feature & RNPM_FLAG_DELAY_UPDATE_VLAN_TABLE) { + /* this port try to delete a vlan table */ + // todo + update_pf_vlan(adapter); + + adapter->flags_feature &= (~RNPM_FLAG_DELAY_UPDATE_VLAN_TABLE); + } + + if (adapter->flags_feature & RNPM_FLAG_DELAY_UPDATE_MUTICAST_TABLE) { + // update multicast table + // todo + adapter->flags_feature &= + (~RNPM_FLAG_DELAY_UPDATE_MUTICAST_TABLE); + } +} + +/** + * rnpm_pf_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +void rnpm_pf_service_task(struct work_struct *work) +{ + struct rnpm_pf_adapter *pf_adapter = + container_of(work, struct rnpm_pf_adapter, service_task); + + if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) + return; + + /* reset pf */ + if (test_and_clear_bit(RNPM_PF_RESET, &pf_adapter->flags)) + rnpm_pf_reset_subtask(pf_adapter); + + /* set mtu */ + if (test_and_clear_bit(RNPM_PF_SET_MTU, &pf_adapter->flags)) + rnpm_pf_mtu_subtask(pf_adapter); + + /* when up/down need delay get link stat on next time */ + if (test_and_clear_bit(RNPM_PF_SERVICE_SKIP_HANDLE, + &pf_adapter->flags)) { + return; + } + + if (test_bit(RNPM_PF_LINK_CHANGE, &pf_adapter->flags)) { + if (rnpm_pf_get_port_link_stat(pf_adapter) < 0) + set_bit(RNPM_PF_LINK_CHANGE, &pf_adapter->flags); + else + clear_bit(RNPM_PF_LINK_CHANGE, &pf_adapter->flags); + } +} + +/** + * rnpm_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +void rnpm_service_task(struct work_struct *work) +{ + struct rnpm_adapter *adapter = + container_of(work, struct rnpm_adapter, service_task); + + rnpm_reset_subtask(adapter); + rnpm_sfp_detection_subtask(adapter); + rnpm_sfp_link_config_subtask(adapter); + rnpm_watchdog_subtask(adapter); + rnpm_rx_len_reset_subtask(adapter); + rnpm_check_hang_subtask(adapter); + rnpm_service_event_complete(adapter); +} + +static int rnpm_tso(struct rnpm_ring *tx_ring, struct rnpm_tx_buffer *first, + u8 *hdr_len) +{ + struct rnpm_adapter *adapter = netdev_priv(tx_ring->netdev); + struct sk_buff *skb = first->skb; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + union { + struct tcphdr *tcp; + struct udphdr *udp; + unsigned char *hdr; + } l4; + u32 paylen, l4_offset; + int err; + u8 *inner_mac; + u16 gso_segs, gso_size; + u16 gso_need_pad; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (!skb_is_gso(skb)) + return 0; + + err = skb_cow_head(skb, 0); + if (err < 0) + return err; + + inner_mac = skb->data; + ip.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + first->tx_flags |= + RNPM_TXD_FLAG_TSO | RNPM_TXD_IP_CSUM | RNPM_TXD_L4_CSUM; + + /* initialize outer IP header fields */ + if (ip.v4->version == 4) { + /* IP header will have to cancel out any data that + * is not a part of the outer IP header + */ + ip.v4->check = 0x0000; + } else { + ip.v6->payload_len = 0; + } +#ifdef HAVE_ENCAP_TSO_OFFLOAD + if (skb_shinfo(skb)->gso_type & + (SKB_GSO_GRE | +#ifdef NETIF_F_GSO_PARTIAL + SKB_GSO_GRE_CSUM | +#endif + SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) { +#ifndef NETIF_F_GSO_PARTIAL + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) { +#else + if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) && + (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) { +#endif + } + /* we should always do this */ + inner_mac = skb_inner_mac_header(skb); + + first->tunnel_hdr_len = (inner_mac - skb->data); + if (skb_shinfo(skb)->gso_type & + (SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) { + first->tx_flags |= RNPM_TXD_TUNNEL_VXLAN; + l4.udp->check = 0; + tx_dbg("set outer l4.udp to 0\n"); + } else { + first->tx_flags |= RNPM_TXD_TUNNEL_NVGRE; + } + + /* reset pointers to inner headers */ + ip.hdr = skb_inner_network_header(skb); + l4.hdr = skb_inner_transport_header(skb); + } + +#endif /* HAVE_ENCAP_TSO_OFFLOAD */ + if (ip.v4->version == 4) { + /* IP header will have to cancel out any data that + * is not a part of the outer IP header + */ + ip.v4->check = 0x0000; + + } else { + ip.v6->payload_len = 0; + /* set ipv6 type */ + + first->tx_flags |= (RNPM_TXD_FLAG_IPv6); + } + + /* determine offset of inner transport header */ + l4_offset = l4.hdr - skb->data; + + paylen = skb->len - l4_offset; + tx_dbg("before l4 checksum is %x\n", l4.tcp->check); + + // csum_replace_by_diff(&l4.tcp->check, htonl(paylen)); + + tx_dbg("l4 checksum is %x\n", l4.tcp->check); + + if (skb->csum_offset == offsetof(struct tcphdr, check)) { + tx_dbg("tcp before l4 checksum is %x\n", l4.tcp->check); + // first->tx_flags |= RNP_TXD_L4_TYPE_TCP; + first->tx_flags |= RNPM_TXD_L4_TYPE_TCP; + /* compute length of segmentation header */ + *hdr_len = (l4.tcp->doff * 4) + l4_offset; + csum_replace_by_diff(&l4.tcp->check, + (__force __wsum)htonl(paylen)); + tx_dbg("tcp l4 checksum is %x\n", l4.tcp->check); + // we should clear tcp.flags.push flas + l4.tcp->psh = 0; + } else { + tx_dbg("paylen is %x\n", paylen); + // first->tx_flags |= RNP_TXD_L4_TYPE_UDP; + first->tx_flags |= RNPM_TXD_L4_TYPE_UDP; + /* compute length of segmentation header */ + tx_dbg("udp before l4 checksum is %x\n", l4.udp->check); + *hdr_len = sizeof(*l4.udp) + l4_offset; + csum_replace_by_diff(&l4.udp->check, + (__force __wsum)htonl(paylen)); + tx_dbg("udp l4 checksum is %x\n", l4.udp->check); + } + + first->mac_ip_len = l4.hdr - ip.hdr; + first->mac_ip_len |= (ip.hdr - inner_mac) << 9; + /* pull values out of skb_shinfo */ + gso_size = skb_shinfo(skb)->gso_size; + gso_segs = skb_shinfo(skb)->gso_segs; + + if (adapter->priv_flags & RNPM_PRIV_FLAG_TX_PADDING) { + gso_need_pad = (first->skb->len - *hdr_len) % gso_size; + if (gso_need_pad) { + if ((gso_need_pad + *hdr_len) <= + tx_ring->gso_padto_bytes) { + gso_need_pad = tx_ring->gso_padto_bytes - + (gso_need_pad + *hdr_len); + first->gso_need_padding = !!gso_need_pad; + } + } + } + /* update gso size and bytecount with header size */ + /* to fix tx status */ + first->gso_segs = gso_segs; + first->bytecount += (first->gso_segs - 1) * *hdr_len; + + first->mss_len_vf_num |= (gso_size | ((l4.tcp->doff * 4) << 24)); + // rnpm_tx_ctxtdesc(tx_ring,skb_shinfo(skb)->gso_size ,l4len, 0, 0, + // type_tucmd); + + first->ctx_flag = true; + return 1; +} + +__maybe_unused static void set_resevd(struct rnpm_tx_buffer *first) +{ + struct sk_buff *skb = first->skb; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + union { + struct tcphdr *tcp; + struct udphdr *udp; + unsigned char *hdr; + } l4 __maybe_unused; + + ip.hdr = skb_network_header(skb); + + if (ip.v4->version == 4) { + u16 old = ip.v4->frag_off; + + ip.v4->frag_off |= 0x0080; + // l4_proto = ip.v4->protocol; + // first->cmd_flags |= RNP_TXD_FLAG_IPv4; + + csum_replace_by_diff(&ip.v4->check, ip.v4->frag_off - old); + } +} + +static int rnpm_tx_csum(struct rnpm_ring *tx_ring, struct rnpm_tx_buffer *first) +{ + struct sk_buff *skb = first->skb; + struct rnpm_adapter *adapter = netdev_priv(tx_ring->netdev); + u8 l4_proto = 0; + u8 ip_len = 0; + u8 mac_len = 0; + u8 *inner_mac = skb->data; + u8 *exthdr; + __be16 frag_off; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + union { + struct tcphdr *tcp; + struct udphdr *udp; + unsigned char *hdr; + } l4; + + if (adapter->priv_flags & RNPM_PRIV_FLAG_TX_PADDING) { + /* Skb is sctp and len < 60 bytes, need to open mac padding */ + if (tx_ring->gso_padto_bytes != 60) + first->gso_need_padding = true; + } + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + ip.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + inner_mac = skb->data; + + /* outer protocol */ + if (skb->encapsulation) { + /* define outer network header type */ + if (ip.v4->version == 4) { + l4_proto = ip.v4->protocol; + } else { + exthdr = ip.hdr + sizeof(*ip.v6); + l4_proto = ip.v6->nexthdr; + if (l4.hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, + &l4_proto, &frag_off); + } + + /* define outer transport */ + switch (l4_proto) { + case IPPROTO_UDP: + l4.udp->check = 0; + + first->tx_flags |= RNPM_TXD_TUNNEL_VXLAN; + break; +#ifdef HAVE_GRE_ENCAP_OFFLOAD + case IPPROTO_GRE: + first->tx_flags |= RNPM_TXD_TUNNEL_NVGRE; + /* There was a long-standing issue in GRE where GSO + * was not setting the outer transport header unless + * a GRE checksum was requested. This was fixed in + * the 4.6 version of the kernel. In the 4.7 kernel + * support for GRE over IPv6 was added to GSO. So we + * can assume this workaround for all IPv4 headers + * without impacting later versions of the GRE. + */ + if (ip.v4->version == 4) + l4.hdr = ip.hdr + (ip.v4->ihl * 4); + break; +#endif + default: + skb_checksum_help(skb); + return -1; + } + + /* switch IP header pointer from outer to inner header */ + ip.hdr = skb_inner_network_header(skb); + l4.hdr = skb_inner_transport_header(skb); + + inner_mac = skb_inner_mac_header(skb); + first->tunnel_hdr_len = inner_mac - skb->data; + first->ctx_flag = true; + tx_dbg("tunnel length is %d\n", first->tunnel_hdr_len); + } + + mac_len = (ip.hdr - inner_mac); // mac length + tx_dbg("inner checksum needed %d", skb_checksum_start_offset(skb)); + tx_dbg("skb->encapsulation %d\n", skb->encapsulation); + ip_len = (l4.hdr - ip.hdr); + if (ip.v4->version == 4) { + l4_proto = ip.v4->protocol; + // first->cmd_flags |= RNPM_TXD_FLAG_IPv4; + } else { + exthdr = ip.hdr + sizeof(*ip.v6); + l4_proto = ip.v6->nexthdr; + if (l4.hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, + &frag_off); + first->tx_flags |= RNPM_TXD_FLAG_IPv6; + } + /* Enable L4 checksum offloads */ + switch (l4_proto) { + case IPPROTO_TCP: + first->tx_flags |= RNPM_TXD_L4_TYPE_TCP | RNPM_TXD_L4_CSUM; + break; + case IPPROTO_SCTP: + tx_dbg("sctp checksum packet\n"); + first->tx_flags |= RNPM_TXD_L4_TYPE_SCTP | RNPM_TXD_L4_CSUM; + break; + case IPPROTO_UDP: + first->tx_flags |= RNPM_TXD_L4_TYPE_UDP | RNPM_TXD_L4_CSUM; + break; + default: + skb_checksum_help(skb); + return 0; + } + + tx_dbg("mac length is %d\n", mac_len); + tx_dbg("ip length is %d\n", ip_len); + first->mac_ip_len = (mac_len << 9) | ip_len; + return 0; +} +static int __rnpm_maybe_stop_tx(struct rnpm_ring *tx_ring, u16 size) +{ + tx_dbg("stop subqueue\n"); + netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); + /* Herbert's original patch had: + * smp_mb__after_netif_stop_queue(); + * but since that doesn't exist yet, just open code it + */ + smp_mb(); + + /* We need to check again in a case another CPU has just + * made room available + */ + if (likely(rnpm_desc_unused(tx_ring) < size)) + return -EBUSY; + + /* A reprieve! - use start_queue because it doesn't call schedule */ + netif_start_subqueue(tx_ring->netdev, tx_ring->queue_index); + ++tx_ring->tx_stats.restart_queue; + return 0; +} + +static inline int rnpm_maybe_stop_tx(struct rnpm_ring *tx_ring, u16 size) +{ + if (likely(rnpm_desc_unused(tx_ring) >= size)) + return 0; + return __rnpm_maybe_stop_tx(tx_ring, size); +} + +static int rnpm_tx_map(struct rnpm_ring *tx_ring, struct rnpm_tx_buffer *first, + const u8 hdr_len) +{ + struct sk_buff *skb = first->skb; + struct rnpm_tx_buffer *tx_buffer; + struct rnpm_tx_desc *tx_desc; + skb_frag_t *frag; + dma_addr_t dma; + unsigned int data_len, size; + + u32 tx_flags = first->tx_flags; + u32 mac_ip_len = (first->mac_ip_len) << 16; + u16 i = tx_ring->next_to_use; + u64 fun_id = ((u64)(tx_ring->pfvfnum) << (56)); + + tx_desc = RNPM_TX_DESC(tx_ring, i); + + size = skb_headlen(skb); + data_len = skb->data_len; + + dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE); + + tx_buffer = first; + + for (frag = &skb_shinfo(skb)->frags[0];; frag++) { + if (dma_mapping_error(tx_ring->dev, dma)) + goto dma_error; + + /* record length, and DMA address */ + dma_unmap_len_set(tx_buffer, len, size); + dma_unmap_addr_set(tx_buffer, dma, dma); + + // 1st desc + tx_desc->pkt_addr = cpu_to_le64(dma | fun_id); + + while (unlikely(size > RNPM_MAX_DATA_PER_TXD)) { + tx_desc->vlan_cmd = cpu_to_le32(tx_flags); + tx_desc->blen_mac_ip_len = + cpu_to_le32(mac_ip_len ^ RNPM_MAX_DATA_PER_TXD); + //==== desc== + buf_dump_line("tx0 ", __LINE__, tx_desc, + sizeof(*tx_desc)); + i++; + tx_desc++; + if (i == tx_ring->count) { + tx_desc = RNPM_TX_DESC(tx_ring, 0); + i = 0; + } + dma += RNPM_MAX_DATA_PER_TXD; + size -= RNPM_MAX_DATA_PER_TXD; + + tx_desc->pkt_addr = cpu_to_le64(dma | fun_id); + } + + buf_dump_line("tx1 ", __LINE__, tx_desc, sizeof(*tx_desc)); + if (likely(!data_len)) // if not sg break + break; + tx_desc->vlan_cmd = cpu_to_le32(tx_flags); + tx_desc->blen_mac_ip_len = cpu_to_le32(mac_ip_len ^ size); + buf_dump_line("tx2 ", __LINE__, tx_desc, sizeof(*tx_desc)); + + //==== frag== + i++; + tx_desc++; + if (i == tx_ring->count) { + tx_desc = RNPM_TX_DESC(tx_ring, 0); + i = 0; + } + // tx_desc->cmd = RNPM_TXD_CMD_RS; + // tx_desc->mac_ip_len = 0; + + size = skb_frag_size(frag); + + data_len -= size; + + dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size, + DMA_TO_DEVICE); + + tx_buffer = &tx_ring->tx_buffer_info[i]; + } + + /* write last descriptor with RS and EOP bits */ + tx_desc->vlan_cmd = + cpu_to_le32(tx_flags | RNPM_TXD_CMD_EOP | RNPM_TXD_CMD_RS); + tx_desc->blen_mac_ip_len = cpu_to_le32(mac_ip_len ^ size); + + // count++; + + buf_dump_line("tx3 ", __LINE__, tx_desc, sizeof(*tx_desc)); + + /* set the timestamp */ + first->time_stamp = jiffies; + + // tx_ring->tx_stats.send_bytes += first->bytecount; +#ifdef NO_BQL_TEST +#else + netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); +#endif + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. (Only applicable for weak-ordered + * memory model archs, such as IA-64). + * + * We also need this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + + /* set next_to_watch value indicating a packet is present */ + wmb(); + first->next_to_watch = tx_desc; + + // buf_dump_line("tx4 ", __LINE__, tx_desc, sizeof(*tx_desc)); + i++; + if (i == tx_ring->count) + i = 0; + + tx_ring->next_to_use = i; + /* need this */ + rnpm_maybe_stop_tx(tx_ring, DESC_NEEDED); + if (netif_xmit_stopped(txring_txq(tx_ring)) || !netdev_xmit_more()) { + tx_ring->tx_stats.send_bytes_to_hw += first->bytecount; + tx_ring->tx_stats.send_bytes_to_hw += + tx_ring->tx_stats.todo_update; + tx_ring->tx_stats.todo_update = 0; + rnpm_wr_reg(tx_ring->tail, i); + } else { + tx_ring->tx_stats.todo_update = first->bytecount; + } + + return 0; +dma_error: + dev_err(tx_ring->dev, "TX DMA map failed\n"); + + /* clear dma mappings for failed tx_buffer_info map */ + for (;;) { + tx_buffer = &tx_ring->tx_buffer_info[i]; + rnpm_unmap_and_free_tx_resource(tx_ring, tx_buffer); + if (tx_buffer == first) + break; + if (i == 0) + i = tx_ring->count; + i--; + } + + tx_ring->next_to_use = i; + + return -1; +} +__maybe_unused static void rnpm_atr(struct rnpm_ring *ring, + struct rnpm_tx_buffer *first) +{ +} + +netdev_tx_t rnpm_xmit_frame_ring(struct sk_buff *skb, + struct rnpm_adapter *adapter, + struct rnpm_ring *tx_ring) +{ + struct rnpm_tx_buffer *first; + int tso; + u32 tx_flags = 0; + unsigned short f; + u16 count = TXD_USE_COUNT(skb_headlen(skb)); + __be16 protocol = skb->protocol; + u8 hdr_len = 0; + + tx_dbg("=== begin ====\n"); + tx_dbg("skb:%p, skb->len:%d headlen:%d, data_len:%d\n", skb, skb->len, + skb_headlen(skb), skb->data_len); + /* need: 1 descriptor per page * PAGE_SIZE/RNPM_MAX_DATA_PER_TXD, + * + 1 desc for skb_headlen/RNPM_MAX_DATA_PER_TXD, + * + 2 desc gap to keep tail from touching head, + * + 1 desc for context descriptor, + * otherwise try next time + */ + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) { + skb_frag_t *frag_temp = &skb_shinfo(skb)->frags[f]; + + count += TXD_USE_COUNT(skb_frag_size(frag_temp)); + tx_dbg(" #%d frag: size:%d\n", f, skb_frag_size(frag_temp)); + if (count > 60) { + /* error detect */ + netdev_dbg(adapter->netdev, "desc too large, %d\n", + count); + return NETDEV_TX_BUSY; + } + } + + if (rnpm_maybe_stop_tx(tx_ring, count + 3)) { + tx_ring->tx_stats.tx_busy++; + return NETDEV_TX_BUSY; + } + + /* record the location of the first descriptor for this packet */ + first = &tx_ring->tx_buffer_info[tx_ring->next_to_use]; + first->skb = skb; + first->bytecount = skb->len; + first->gso_segs = 1; + first->type_tucmd = 0; + + /* default len should not 0 (hw request) */ + first->mac_ip_len = 20; + first->mss_len_vf_num = 0; + first->inner_vlan_tunnel_len = 0; + +#ifdef RNPM_IOV_VEB_BUG_NOT_FIXED + first->ctx_flag = + (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) ? true : false; +#else + first->ctx_flag = false; +#endif + if (adapter->priv_flags & RNPM_PRIV_FLAG_TX_PADDING) + first->ctx_flag = true; + /* if we have a HW VLAN tag being added default to the HW one */ + /* RNPM_TXD_VLAN_VALID is used for veb */ + if (adapter->flags2 & RNPM_FLAG2_VLAN_STAGS_ENABLED) { + /* always add a stags for any packets out */ + tx_flags |= adapter->stags_vid; + tx_flags |= RNPM_TXD_VLAN_CTRL_INSERT_VLAN; + if (skb_vlan_tag_present(skb)) { + tx_flags |= RNPM_TXD_VLAN_VALID; + first->inner_vlan_tunnel_len |= + (skb_vlan_tag_get(skb) << 8); + first->ctx_flag = true; + } else if (protocol == htons(ETH_P_8021Q)) { + struct vlan_hdr *vhdr, _vhdr; + + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), + &_vhdr); + if (!vhdr) + goto out_drop; + + protocol = vhdr->h_vlan_encapsulated_proto; + // tx_flags |= ntohs(vhdr->h_vlan_TCI); + tx_flags |= RNPM_TXD_VLAN_VALID; + } + } else { + /* normal mode */ + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb); + tx_flags |= RNPM_TXD_VLAN_VALID | + RNPM_TXD_VLAN_CTRL_INSERT_VLAN; + tx_ring->tx_stats.vlan_add++; + /* else if it is a SW VLAN check the next protocol and store the tag + */ + } else if (protocol == htons(ETH_P_8021Q)) { + struct vlan_hdr *vhdr, _vhdr; + + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), + &_vhdr); + if (!vhdr) + goto out_drop; + + protocol = vhdr->h_vlan_encapsulated_proto; + tx_flags |= ntohs(vhdr->h_vlan_TCI); + tx_flags |= RNPM_TXD_VLAN_VALID; + } + } + protocol = vlan_get_protocol(skb); + + skb_tx_timestamp(skb); + /* just for test */ + // tx_flags |= RNPM_TXD_FLAG_PTP; +#ifdef SKB_SHARED_TX_IS_UNION + if (unlikely(skb_tx(skb)->hardware) && + adapter->flags2 & RNPM_FLAG2_PTP_ENABLED && adapter->ptp_tx_en) { + if (!test_and_set_bit_lock(__RNPM_PTP_TX_IN_PROGRESS, + &adapter->state)) { + skb_tx(skb)->in_progress = 1; + +#else + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + adapter->flags2 & RNPM_FLAG2_PTP_ENABLED && adapter->ptp_tx_en) { + if (!test_and_set_bit_lock(__RNPM_PTP_TX_IN_PROGRESS, + &adapter->state)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; +#endif + tx_flags |= RNPM_TXD_FLAG_PTP; + adapter->ptp_tx_skb = skb_get(skb); + adapter->tx_hwtstamp_start = jiffies; + schedule_work(&adapter->tx_hwtstamp_work); + + } else { + netdev_dbg(adapter->netdev, "ptp_tx_skb miss\n"); + } + } + /* record initial flags and protocol */ + first->tx_flags = tx_flags; + first->protocol = protocol; + + tso = rnpm_tso(tx_ring, first, &hdr_len); + if (tso < 0) + goto out_drop; + else if (!tso) + rnpm_tx_csum(tx_ring, first); + // set_resevd(first); + /* check sriov mode */ + /* in this mode pf send msg should with vf_num */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + first->ctx_flag = true; + first->mss_len_vf_num |= (adapter->vf_num_for_pf << 16); + } + + /* send this packet to rpu */ + if (adapter->priv_flags & RNPM_PRIV_FLAG_TO_RPU) { + first->ctx_flag = true; + first->type_tucmd |= RNPM_TXD_FLAG_TO_RPU; + } + + /* add control desc */ + rnpm_maybe_tx_ctxtdesc(tx_ring, first, first->type_tucmd); + if (rnpm_tx_map(tx_ring, first, hdr_len)) + goto cleanup_tx_tstamp; + tx_dbg("=== end ====\n\n\n\n"); + return NETDEV_TX_OK; + +out_drop: + dev_kfree_skb_any(first->skb); + first->skb = NULL; +cleanup_tx_tstamp: + + if (unlikely(tx_flags & RNPM_TXD_FLAG_PTP)) { + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + cancel_work_sync(&adapter->tx_hwtstamp_work); + clear_bit_unlock(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state); + } + + return NETDEV_TX_OK; +} + +static u8 skb_need_padto_bytes(struct sk_buff *skb, bool mac_padding) +{ + u8 l4_proto = 0; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + + if (mac_padding) { + ip.hdr = skb_network_header(skb); + l4_proto = + ip.v4->version == 4 ? ip.v4->protocol : ip.v6->nexthdr; + /* Skb is sctp and len < 60 bytes, need to open mac padding */ + if ((l4_proto == IPPROTO_SCTP) && (skb->len < 60)) + return 33; + return 60; + } + + return 33; +} + +static netdev_tx_t rnpm_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_ring *tx_ring; + u8 padto_bytes; + + if (!netif_carrier_ok(netdev)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* The minimum packet size for olinfo paylen is 17 so pad the skb + * in order to meet this minimum size requirement. + */ + padto_bytes = skb_need_padto_bytes(skb, !!(adapter->priv_flags & + RNPM_PRIV_FLAG_TX_PADDING)); + if (skb_put_padto(skb, padto_bytes)) + return NETDEV_TX_OK; + + /* for sctp packet , padding 0 change the crc32c */ + /* mac can padding (17-63) length to 64 */ + tx_ring = adapter->tx_ring[skb->queue_mapping]; + tx_ring->gso_padto_bytes = padto_bytes; + + return rnpm_xmit_frame_ring(skb, adapter, tx_ring); +} + +/** + * rnpm_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int rnpm_set_mac(struct net_device *netdev, void *p) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct sockaddr *addr = p; + + dbg("[%s] call set mac\n", netdev->name); + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, addr->sa_data); + // memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); + + hw->mac.ops.set_rar(hw, adapter->uc_off, hw->mac.addr, VMDQ_P(0), + RNPM_RAH_AV); + + /* setup mac unicast filters */ + if (hw->mac.mc_location == rnpm_mc_location_mac) { + hw->mac.ops.set_rar_mac(hw, 0, hw->mac.addr, VMDQ_P(0), + adapter->port); + } + + rnpm_configure_virtualization(adapter); + return 0; +} + +static int rnpm_mdio_read(struct net_device *netdev, int prtad, int devad, + u16 addr) +{ + int rc = -EIO; + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + u16 value; + + rc = hw->phy.ops.read_reg(hw, addr, 0, &value); + if (!rc) + rc = value; + + return rc; +} + +__maybe_unused static int rnpm_mdio_write(struct net_device *netdev, int prtad, + int devad, u16 addr, u16 value) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + return hw->phy.ops.write_reg(hw, addr, 0, value); +} + +static int rnpm_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr->ifr_data; + int prtad, devad, ret = -EIO; + + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + // if (hw->phy.media_type != rnpm_media_type_copper) + // return -EOPNOTSUPP; + + prtad = (mii->phy_id & MDIO_PHY_ID_PRTAD) >> 5; + devad = (mii->phy_id & MDIO_PHY_ID_DEVAD); + + switch (cmd) { + case SIOCGMIIPHY: + mii->phy_id = hw->phy.phy_addr; + break; + case SIOCGMIIREG: + ret = rnpm_mdio_read(netdev, prtad, devad, mii->reg_num); + if (ret < 0) + return ret; + mii->val_out = ret; + break; + case SIOCSMIIREG: + // return rnpm_mdio_write(netdev, prtad, devad, mii->reg_num, + // mii->val_in); break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int rnpm_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) +{ +#ifdef HAVE_PTP_1588_CLOCK + struct rnpm_adapter *adapter = netdev_priv(netdev); +#endif + + /* ptp 1588 used this */ + switch (cmd) { +#ifdef HAVE_PTP_1588_CLOCK +#ifdef SIOCGHWTSTAMP + case SIOCGHWTSTAMP: +#ifndef NO_PTP + if (module_enable_ptp) + return rnpm_ptp_get_ts_config(adapter, req); +#endif +#endif + break; + case SIOCSHWTSTAMP: +#ifndef NO_PTP + if (module_enable_ptp) + return rnpm_ptp_set_ts_config(adapter, req); + break; +#endif +#endif + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return rnpm_mii_ioctl(netdev, req, cmd); + } + return -EINVAL; +} + +#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 rnpm_netpoll(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + int i; + + /* if interface is down do nothing */ + if (test_bit(__RNPM_DOWN, &adapter->state)) + return; + + adapter->flags |= RNPM_FLAG_IN_NETPOLL; + for (i = 0; i < adapter->num_q_vectors; i++) + rnpm_msix_clean_rings(0, adapter->q_vector[i]); + adapter->flags &= ~RNPM_FLAG_IN_NETPOLL; +} + +#endif + +static void rnpm_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + int i; + + rcu_read_lock(); + + for (i = 0; i < adapter->num_rx_queues; i++) { + struct rnpm_ring *ring = READ_ONCE(adapter->rx_ring[i]); + u64 bytes, packets; + unsigned int start; + + if (ring) { + do { + start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; + } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->rx_packets += packets; + stats->rx_bytes += bytes; + } + } + + for (i = 0; i < adapter->num_tx_queues; i++) { + struct rnpm_ring *ring = READ_ONCE(adapter->tx_ring[i]); + u64 bytes, packets; + unsigned int start; + + if (ring) { + do { + start = u64_stats_fetch_begin(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; + } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->tx_packets += packets; + stats->tx_bytes += bytes; + } + } + + rcu_read_unlock(); + /* following stats updated by rnpm_watchdog_task() */ + stats->multicast = netdev->stats.multicast; + stats->rx_errors = netdev->stats.rx_errors; + stats->rx_dropped = netdev->stats.rx_dropped; + stats->rx_crc_errors = netdev->stats.rx_crc_errors; +} + +#ifdef CONFIG_RNPM_DCB +/** + * rnpm_validate_rtr - verify 802.1Qp to Rx packet buffer mapping is valid. + * @adapter: pointer to rnpm_adapter + * @tc: number of traffic classes currently enabled + * + * Configure a valid 802.1Qp to Rx packet buffer mapping ie confirm + * 802.1Q priority maps to a packet buffer that exists. + */ +static void rnpm_validate_rtr(struct rnpm_adapter *adapter, u8 tc) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 reg, rsave; + int i; + +} + +/** + * rnpm_set_prio_tc_map - Configure netdev prio tc map + * @adapter: Pointer to adapter struct + * + * Populate the netdev user priority to tc map + */ +static void rnpm_set_prio_tc_map(struct rnpm_adapter *adapter) +{ + struct net_device *dev = adapter->netdev; + +} + +#endif /* CONFIG_RNPM_DCB */ +/** + * rnpm_setup_tc - configure net_device for multiple traffic classes + * + * @netdev: net device to configure + * @tc: number of traffic classes to enable + */ +int rnpm_setup_tc(struct net_device *dev, u8 tc) +{ + int err = 0; + struct rnpm_adapter *adapter = netdev_priv(dev); + struct rnpm_hw *hw = &adapter->hw; + + /* Hardware supports up to 8 traffic classes */ + if (tc > RNPM_MAX_TCS_NUM) + return -EINVAL; + + /* Hardware has to reinitialize queues and interrupts to + * match packet buffer alignment. Unfortunately, the + * hardware is not flexible enough to do this dynamically. + */ + while (test_and_set_bit(__RNPM_RESETTING, &adapter->pf_adapter->state)) + usleep_range(1000, 2000); + + if (netif_running(dev)) + rnpm_close(dev); + + rnpm_clear_interrupt_scheme(adapter); + hw->mac.ops.clear_hw_cntrs(hw); + rnpm_update_stats(adapter); + +#ifdef CONFIG_RNPM_DCB + if (tc) { + netdev_set_num_tc(dev, tc); + rnpm_set_prio_tc_map(adapter); + + adapter->flags |= RNPM_FLAG_DCB_ENABLED; + + if (adapter->hw.mac.type == rnpm_mac_82598EB) { + adapter->last_lfc_mode = adapter->hw.fc.requested_mode; + adapter->hw.fc.requested_mode = rnpm_fc_none; + } + } else { + netdev_reset_tc(dev); + + if (adapter->hw.mac.type == rnpm_mac_82598EB) + adapter->hw.fc.requested_mode = adapter->last_lfc_mode; + + adapter->flags &= ~RNPM_FLAG_DCB_ENABLED; + + adapter->temp_dcb_cfg.pfc_mode_enable = false; + adapter->dcb_cfg.pfc_mode_enable = false; + } + + rnpm_validate_rtr(adapter, tc); + +#endif /* CONFIG_RNPM_DCB */ + rnpm_init_interrupt_scheme(adapter); + + /* rss table must reset */ + adapter->rss_tbl_setup_flag = 0; + + if (netif_running(dev)) + err = rnpm_open(dev); + // return rnpm_open(dev); + + clear_bit(__RNPM_RESETTING, &adapter->pf_adapter->state); + return err; +} + +#ifdef CONFIG_PCI_IOV +void rnpm_sriov_reinit(struct rnpm_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + rtnl_lock(); + rnpm_setup_tc(netdev, netdev_get_num_tc(netdev)); + rtnl_unlock(); +} +#endif + +void rnpm_do_reset(struct net_device *netdev) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + if (netif_running(netdev)) + rnpm_reinit_locked(adapter); + else + rnpm_reset(adapter); +} + +static netdev_features_t rnpm_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + /* If Rx checksum is disabled, then RSC/LRO should also be disabled */ + if (!(features & NETIF_F_RXCSUM)) + features &= ~NETIF_F_LRO; + + /* close rx csum when rx fcs on */ + if (features & NETIF_F_RXFCS) + features &= (~NETIF_F_RXCSUM); + /* Turn off LRO if not RSC capable */ + if (!(adapter->flags2 & RNPM_FLAG2_RSC_CAPABLE)) + features &= ~NETIF_F_LRO; + + return features; +} + +static int rnpm_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + netdev_features_t changed = netdev->features ^ features; + bool need_reset = false; + struct rnpm_hw *hw = &adapter->hw; + + switch (features & NETIF_F_NTUPLE) { + case NETIF_F_NTUPLE: + /* turn off ATR, enable perfect filters and reset */ + if (!(adapter->flags & RNPM_FLAG_FDIR_PERFECT_CAPABLE)) + need_reset = true; + + adapter->flags &= ~RNPM_FLAG_FDIR_HASH_CAPABLE; + adapter->flags |= RNPM_FLAG_FDIR_PERFECT_CAPABLE; + break; + default: + /* turn off perfect filters, enable ATR and reset */ + if (adapter->flags & RNPM_FLAG_FDIR_PERFECT_CAPABLE) + need_reset = true; + + adapter->flags &= ~RNPM_FLAG_FDIR_PERFECT_CAPABLE; + + /* We cannot enable ATR if SR-IOV is enabled */ + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) + break; + + /* We cannot enable ATR if we have 2 or more traffic classes */ + if (netdev_get_num_tc(netdev) > 1) + break; + + /* A sample rate of 0 indicates ATR disabled */ + if (!adapter->atr_sample_rate) + break; + + adapter->flags |= RNPM_FLAG_FDIR_HASH_CAPABLE; + break; + } + + /* vlan filter changed */ + if (changed & (NETIF_F_HW_VLAN_CTAG_FILTER)) { + if (features & NETIF_F_HW_VLAN_CTAG_FILTER) + rnpm_vlan_filter_enable(adapter); + else + rnpm_vlan_filter_disable(adapter); + } + /* rss hash changed */ + /* should set rss table to all 0 */ + if (changed & (NETIF_F_RXHASH)) { + if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) { + /* in mutiport mode ,use rss table to zero instead close hw flags */ + if (features & (NETIF_F_RXHASH)) { + adapter->flags &= (~RNPM_FLAG_RXHASH_DISABLE); + rnpm_store_reta(adapter); + } else { + adapter->flags |= RNPM_FLAG_RXHASH_DISABLE; + rnpm_store_reta(adapter); + } + + } else { + u32 iov_en = + (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) ? + RNPM_IOV_ENABLED : + 0; + /* close rxhash will lead all rx packets to ring 0 */ + if (features & (NETIF_F_RXHASH)) + wr32(hw, RNPM_ETH_RSS_CONTROL, + RNPM_ETH_ENABLE_RSS_ONLY | iov_en); + else + wr32(hw, RNPM_ETH_RSS_CONTROL, + RNPM_ETH_DISABLE_RSS | iov_en); + } + } + + /* rx fcs changed */ + /* in this mode rx l4/sctp checksum will get error */ + if (changed & NETIF_F_RXFCS) { + u32 old_value; + + rnpm_msg_post_status(adapter, PF_FCS_STATUS); + + old_value = rd32(hw, RNPM_MAC_RX_CFG(adapter->port)); +#define FCS_MASK (0x6) + if (features & NETIF_F_RXFCS) { + old_value &= (~FCS_MASK); + /* if in rx fcs mode , hw rxcsum may error, close rxcusm */ + } else { + old_value |= FCS_MASK; + } + wr32(hw, RNPM_MAC_RX_CFG(adapter->port), + old_value | RNPM_MAX_RX_CFG_IPC); + } + + if (changed & NETIF_F_RXALL) + need_reset = true; + + if (changed & NETIF_F_HW_VLAN_CTAG_RX) { + if (features & NETIF_F_HW_VLAN_CTAG_RX) + rnpm_vlan_strip_enable(adapter); + else + rnpm_vlan_strip_disable(adapter); + } + + /* set up active feature */ + netdev->features = features; + + if (need_reset) + rnpm_do_reset(netdev); + + return 0; +} + +static int rnpm_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, + __always_unused u16 flags, + struct netlink_ext_ack __always_unused *ext) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + struct rnpm_hw *hw = &adapter->hw; + struct nlattr *attr, *br_spec; + int rem; + + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return -EOPNOTSUPP; + + br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + + nla_for_each_nested(attr, br_spec, rem) { + __u16 mode; + // u32 reg = 0; + + if (nla_type(attr) != IFLA_BRIDGE_MODE) + continue; + + mode = nla_get_u16(attr); + if (mode == BRIDGE_MODE_VEPA) { + adapter->flags2 &= ~RNPM_FLAG2_BRIDGE_MODE_VEB; + wr32(hw, RNPM_DMA_CONFIG, + rd32(hw, RNPM_DMA_CONFIG) | DMA_VEB_BYPASS); + } else if (mode == BRIDGE_MODE_VEB) { + adapter->flags2 |= RNPM_FLAG2_BRIDGE_MODE_VEB; + wr32(hw, RNPM_DMA_CONFIG, + rd32(hw, RNPM_DMA_CONFIG) & (~DMA_VEB_BYPASS)); + + } else + return -EINVAL; + + e_info(drv, "enabling bridge mode: %s\n", + mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); + } + + return 0; +} + +static int rnpm_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, + u32 __maybe_unused filter_mask, int nlflags) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + u16 mode; + + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return 0; + + if (adapter->flags2 & RNPM_FLAG2_BRIDGE_MODE_VEB) + mode = BRIDGE_MODE_VEB; + else + mode = BRIDGE_MODE_VEPA; + + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 0, 0, nlflags, + filter_mask, NULL); +} + +void rnpm_clear_udp_tunnel_port(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + + if (!(adapter->flags & (RNPM_FLAG_VXLAN_OFFLOAD_CAPABLE))) + return; + + wr32(hw, RNPM_ETH_VXLAN_PORT, 0); + adapter->vxlan_port = 0; +} + +/** + * rnpm_add_udp_tunnel_port - Get notifications about adding UDP tunnel ports + * @dev: The port's netdev + * @ti: Tunnel endpoint information + **/ +__maybe_unused static void rnpm_add_udp_tunnel_port(struct net_device *dev, + struct udp_tunnel_info *ti) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + struct rnpm_hw *hw = &adapter->hw; + __be16 port = ti->port; + // u32 port_shift = 0; + // u32 reg; + + if (ti->sa_family != AF_INET) + return; + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + if (!(adapter->flags & RNPM_FLAG_VXLAN_OFFLOAD_CAPABLE)) + return; + + if (adapter->vxlan_port == port) + return; + + if (adapter->vxlan_port) { + netdev_info(dev, + "VXLAN port %d set, not adding port %d\n", + ntohs(adapter->vxlan_port), ntohs(port)); + return; + } + + adapter->vxlan_port = port; + break; + default: + return; + } + + wr32(hw, RNPM_ETH_VXLAN_PORT, adapter->vxlan_port); +} + +/** + * rnpm_del_udp_tunnel_port - Get notifications about removing UDP tunnel ports + * @dev: The port's netdev + * @ti: Tunnel endpoint information + **/ +__maybe_unused static void rnpm_del_udp_tunnel_port(struct net_device *dev, + struct udp_tunnel_info *ti) +{ + struct rnpm_adapter *adapter = netdev_priv(dev); + // u32 port_mask; + + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + // ti->type != UDP_TUNNEL_TYPE_GENEVE) + return; + + if (ti->sa_family != AF_INET) + return; + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + if (!(adapter->flags & RNPM_FLAG_VXLAN_OFFLOAD_CAPABLE)) + return; + + if (adapter->vxlan_port != ti->port) { + netdev_info(dev, "VXLAN port %d not found\n", + ntohs(ti->port)); + return; + } + + // port_mask = RNPM_VXLANCTRL_VXLAN_UDPPORT_MASK; + break; + default: + return; + } + + rnpm_clear_udp_tunnel_port(adapter); + adapter->flags2 |= RNPM_FLAG2_UDP_TUN_REREG_NEEDED; +} + +#define RNPM_MAX_TUNNEL_HDR_LEN 80 +#ifdef NETIF_F_GSO_PARTIAL +#define RNPM_MAX_MAC_HDR_LEN 127 +#define RNPM_MAX_NETWORK_HDR_LEN 511 + +static netdev_features_t rnpm_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + unsigned int network_hdr_len, mac_hdr_len; + + /* Make certain the headers can be described by a context descriptor */ + mac_hdr_len = skb_network_header(skb) - skb->data; + if (unlikely(mac_hdr_len > RNPM_MAX_MAC_HDR_LEN)) + return features & + ~(NETIF_F_HW_CSUM | NETIF_F_SCTP_CRC | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_TSO | NETIF_F_TSO6); + + network_hdr_len = skb_checksum_start(skb) - skb_network_header(skb); + if (unlikely(network_hdr_len > RNPM_MAX_NETWORK_HDR_LEN)) + return features & ~(NETIF_F_HW_CSUM | NETIF_F_SCTP_CRC | + NETIF_F_TSO | NETIF_F_TSO6); + + /* We can only support IPV4 TSO in tunnels if we can mangle the + * inner IP ID field, so strip TSO if MANGLEID is not supported. + */ + if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) + features &= ~NETIF_F_TSO; + + return features; +} +#else +static netdev_features_t rnpm_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (!skb->encapsulation) + return features; + + if (unlikely(skb_inner_mac_header(skb) - skb_transport_header(skb) > + RNPM_MAX_TUNNEL_HDR_LEN)) + return features & ~NETIF_F_CSUM_MASK; + + return features; +} + +#endif /* NETIF_F_GSO_PARTIAL */ + +const struct net_device_ops rnpm_netdev_ops = { + .ndo_open = rnpm_open, + .ndo_stop = rnpm_close, + .ndo_start_xmit = rnpm_xmit_frame, + .ndo_set_rx_mode = rnpm_set_rx_mode, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = rnpm_ioctl, + .ndo_change_mtu = rnpm_change_mtu, + .ndo_get_stats64 = rnpm_get_stats64, + .ndo_tx_timeout = rnpm_tx_timeout, + .ndo_set_tx_maxrate = rnpm_tx_maxrate, + .ndo_set_mac_address = rnpm_set_mac, + .ndo_vlan_rx_add_vid = rnpm_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = rnpm_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rnpm_netpoll, +#endif + .ndo_bridge_setlink = rnpm_ndo_bridge_setlink, + .ndo_bridge_getlink = rnpm_ndo_bridge_getlink, + + .ndo_features_check = rnpm_features_check, + .ndo_set_features = rnpm_set_features, + .ndo_fix_features = rnpm_fix_features, +}; + +void rnpm_assign_netdev_ops(struct net_device *dev) +{ + dev->netdev_ops = &rnpm_netdev_ops; + rnpm_set_ethtool_ops(dev); + dev->watchdog_timeo = 5 * HZ; +} + +/** + * rnpm_wol_supported - Check whether device supports WoL + * @hw: hw specific details + * @device_id: the device ID + * @subdev_id: the subsystem device ID + * + * This function is used by probe and ethtool to determine + * which devices have WoL support + * + **/ +int rnpm_wol_supported(struct rnpm_adapter *adapter, u16 device_id, + u16 subdevice_id) +{ + int is_wol_supported = 0; + struct rnpm_hw *hw = &adapter->hw; + + if (hw->wol_supported) + is_wol_supported = 1; + + return is_wol_supported; +} + +static inline unsigned long rnpm_tso_features(void) +{ + unsigned long features = 0; + +#ifdef NETIF_F_TSO + features |= NETIF_F_TSO; +#endif /* NETIF_F_TSO */ +#ifdef NETIF_F_TSO6 + features |= NETIF_F_TSO6; +#endif /* NETIF_F_TSO6 */ +#ifdef NETIF_F_GSO_PARTIAL + features |= NETIF_F_GSO_PARTIAL | RNPM_GSO_PARTIAL_FEATURES; +#endif + + return features; +} + +static int rnpm_rm_adpater(struct rnpm_adapter *adapter) +{ + struct net_device *netdev; + + netdev = adapter->netdev; + rnpm_info("= remove adapter:%s =\n", netdev->name); + // rnpm_logd(LOG_FUNC_ENTER,"= %s: %s\n", __func__, netdev->name); + + rnpm_dbg_adapter_exit(adapter); + + netif_carrier_off(netdev); + + set_bit(__RNPM_DOWN, &adapter->state); + + /* should clean all tx schedule_work */ +#ifndef NO_PTP + if (module_enable_ptp) { + // should wait ptp timeout + while (test_bit(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state)) + usleep_range(10000, 20000); + cancel_work_sync(&adapter->tx_hwtstamp_work); + } +#endif + + cancel_work_sync(&adapter->service_task); + rnpm_sysfs_exit(adapter); + + if (adapter->netdev_registered) { + unregister_netdev(netdev); + adapter->netdev_registered = false; + } + + /* set this used in 4 ports in 1pf mode */ + // adapter->netdev = NULL; + // adapter->rm_mode = true; + + rnpm_clear_interrupt_scheme(adapter); + + rnpm_info("remove %s complete\n", netdev->name); + // rnpm_logd(LOG_FUNC_ENTER,"= remove %s done\n", netdev->name); + + free_netdev(netdev); + + return 0; +} + +/* read from hw */ +void rnpm_fix_queue_number(struct rnpm_hw *hw) +{ + struct rnpm_adapter *adapter = hw->back; + u32 count; + + // count = rd32(hw, RNPM_DMA_STATUS); + // count = (count & DMA_RING_NUM) >> 24; + /* total_queue_pair_cnts equal to 64 on n400 tp & n10 4x10 board , when + * nic-mode 3 and adapter cnt 2 + */ + if ((rnpm_info_tbl[adapter->pf_adapter->board_type]->adapter_cnt == + 2) && + (hw->mode == MODE_NIC_MODE_4PORT)) { + if ((adapter->pf_adapter->board_type == board_n10) || + (adapter->pf_adapter->board_type == board_n400_4x1G)) { + rnpm_info_tbl[adapter->pf_adapter->board_type] + ->total_queue_pair_cnts = 64; + } + } + + count = rnpm_info_tbl[adapter->pf_adapter->board_type] + ->total_queue_pair_cnts / + rnpm_info_tbl[adapter->pf_adapter->board_type]->adapter_cnt; + + if (count != adapter->max_ring_pair_counts) { + netdev_dbg(adapter->netdev, + "reset max_ring_pair_counts from %d to %d\n", + adapter->max_ring_pair_counts, count); + adapter->max_ring_pair_counts = count; + } + +#ifdef RNPM_MAX_RINGS + adapter->max_ring_pair_counts = RNPM_MAX_RINGS; +#endif +} + +static int check_valid_mode(struct rnpm_pf_adapter *pf_adapter) +{ + int err = 0; + + switch (pf_adapter->board_type) { + case board_n10: // port_valid should be valid + case board_n400_4x1G: + return 0; + case board_vu440_2x10G: + // case board_n10_2x10G: + if (pf_adapter->port_valid & (~0x01)) + err = -1; + break; + case board_vu440_4x10G: + // case board_n10_4x10G: + if (pf_adapter->port_valid & (~0x03)) + err = -1; + break; + case board_vu440_8x10G: + // case board_n10_8x10G: + if (pf_adapter->port_valid & (~0x0f)) + err = -1; + break; + default: + rnpm_dbg("board mode error\n"); + err = -1; + break; + } + + return err; +} + +static int rnpm_init_msix_pf_adapter(struct rnpm_pf_adapter *pf_adapter) +{ + int total_msix_counts; + int valid_port = Hamming_weight_1(pf_adapter->port_valid); + int vector, vectors = 0, err, max_msix_counts_per_port; + int min_vectors = valid_port + 1; + int remain, i; +#ifdef NO_PCI_MSIX_COUNT + total_msix_counts = 64; +#else + total_msix_counts = pci_msix_vec_count(pf_adapter->pdev); +#endif + + // reset max vectors if set by kconfig +#ifdef CONFIG_MXGBEM_MSIX_COUNT + total_msix_counts = CONFIG_MXGBEM_MSIX_COUNT; +#endif + if (pf_msix_counts_set) { + total_msix_counts = + pf_msix_counts_set < 5 ? 5 : pf_msix_counts_set; + } + total_msix_counts -= 1; // one for mailbox + total_msix_counts = min_t( + int, + rnpm_info_tbl[pf_adapter->board_type]->total_queue_pair_cnts, + total_msix_counts); + max_msix_counts_per_port = total_msix_counts / valid_port; + + remain = total_msix_counts - max_msix_counts_per_port * valid_port; + + /* decide max msix for each port */ + for (i = 0; i < MAX_PORT_NUM; i++) { + /* this port is valid */ + if (pf_adapter->port_valid & (1 << i)) { + if (remain) { + pf_adapter->max_msix_counts[i] = + max_msix_counts_per_port + 1; + remain--; + } else { + pf_adapter->max_msix_counts[i] = + max_msix_counts_per_port; + } + } + pf_adapter->max_msix_counts[i] = min_t( + int, pf_adapter->max_msix_counts[i], num_online_cpus()); + rnpm_dbg("port %d, max_msix_counts %d\n", i, + pf_adapter->max_msix_counts[i]); + vectors += pf_adapter->max_msix_counts[i]; + } + pf_adapter->other_irq = 0; // mbx use vector0 + vectors += 1; + + pf_adapter->msix_entries = + kcalloc(vectors, sizeof(struct msix_entry), GFP_KERNEL); + if (!pf_adapter->msix_entries) { + rnpm_err("alloc msix_entries failed!\n"); + return -ENOMEM; + } + + for (vector = 0; vector < vectors; vector++) + pf_adapter->msix_entries[vector].entry = vector; + + err = pci_enable_msix_range(pf_adapter->pdev, pf_adapter->msix_entries, + min_vectors, vectors); + + if (err < 0) { + rnpm_err("pci_enable_msix failed: req:%d err:%d\n", vectors, + err); + kfree(pf_adapter->msix_entries); + pf_adapter->msix_entries = NULL; + return -EINVAL; + } else if ((err > 0) && (err != vectors)) { + // should reset msix for each port + rnpm_dbg("get msix count %d\n", err); + total_msix_counts = err; + total_msix_counts -= 1; // one for mailbox + + max_msix_counts_per_port = total_msix_counts / valid_port; + remain = total_msix_counts - + max_msix_counts_per_port * valid_port; + + /* decide max msix for each port */ + for (i = 0; i < MAX_PORT_NUM; i++) { + /* this port is valid */ + if (pf_adapter->port_valid & (1 << i)) { + if (remain) { + pf_adapter->max_msix_counts[i] = + max_msix_counts_per_port + 1; + remain--; + } else { + pf_adapter->max_msix_counts[i] = + max_msix_counts_per_port; + } + } + pf_adapter->max_msix_counts[i] = + min_t(int, pf_adapter->max_msix_counts[i], + num_online_cpus()); + } + } + + return 0; +} + +static int rnpm_rm_msix_pf_adapter(struct rnpm_pf_adapter *pf_adapter) +{ + // free other_irq + pci_disable_msix(pf_adapter->pdev); + kfree(pf_adapter->msix_entries); + pf_adapter->msix_entries = 0; + return 0; +} + +int rnpm_set_clause73_autoneg_enable(struct net_device *netdev, int enable) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + if (!adapter) + return -EINVAL; + + if (test_bit(__RNPM_DOWN, &adapter->state) || + test_bit(__RNPM_RESETTING, &adapter->state)) + return -EBUSY; + + return rnpm_hw_set_clause73_autoneg_enable(&adapter->hw, enable); +} +EXPORT_SYMBOL(rnpm_set_clause73_autoneg_enable); + +static void rnpm_rm_mbx_irq(struct rnpm_pf_adapter *pf_adapter) +{ + pf_adapter->hw.mbx.ops.configure( + &pf_adapter->hw, + pf_adapter->msix_entries[pf_adapter->other_irq].entry, false); + free_irq(pf_adapter->msix_entries[pf_adapter->other_irq].vector, + pf_adapter); + pf_adapter->hw.mbx.irq_enabled = false; +} + +static int rnpm_request_mbx_irq(struct rnpm_pf_adapter *pf_adapter) +{ + int err = 0; + + snprintf(pf_adapter->name, 20, "rnpm%d%d-other%d", + rnpm_is_pf1(pf_adapter->pdev), pf_adapter->bd_number, + pf_adapter->other_irq); + err = request_irq( + pf_adapter->msix_entries[pf_adapter->other_irq].vector, + rnpm_msix_other, 0, pf_adapter->name, pf_adapter); + + if (err) { + err = -1; + goto err_mbx_irq; + } + + pf_adapter->hw.mbx.ops.configure( + &pf_adapter->hw, + pf_adapter->msix_entries[pf_adapter->other_irq].entry, true); + pf_adapter->hw.mbx.irq_enabled = true; + +err_mbx_irq: + return err; +} + +static int rnpm_add_pf_adapter(struct pci_dev *pdev, + struct rnpm_pf_adapter **ppf_adapter, + const struct pci_device_id *id) +{ + /*alloc pf_adapter and set it to pdev priv */ + struct rnpm_pf_adapter *pf_adapter; + int i, err = 0; +#ifdef FT_PADDING + u32 data; +#endif + u8 __iomem *hw_addr_bar0 = 0; + static int pf0_cards_found; + static int pf1_cards_found; + struct rnpm_hw *hw; + struct rnpm_info *ii = rnpm_info_tbl[(int)id->driver_data]; + + pf_adapter = devm_kzalloc(&pdev->dev, sizeof(*pf_adapter), GFP_KERNEL); + if (pf_adapter) { + *ppf_adapter = pf_adapter; + } else { + err = -ENOMEM; + goto err_pf_alloc; + } + + pf_adapter->board_type = (int)id->driver_data; + pf_adapter->pdev = pdev; + pci_set_drvdata(pdev, pf_adapter); + /* map pcie bar */ +#define RNPM_NIC_BAR0 (0) + hw_addr_bar0 = pcim_iomap(pdev, RNPM_NIC_BAR0, 0); + if (!hw_addr_bar0) { + dev_err(&pdev->dev, "pcim_iomap bar%d failed!\n", 0); + goto err_ioremap0; + } +#ifdef FIX_VF_BUG + rnpm_wr_reg(hw_addr_bar0 + + (0x7982fc & + (pci_resource_len(pdev, RNPM_NIC_BAR0) - 1)), + 0); +#else + rnpm_wr_reg(hw_addr_bar0 + + (0x7982fc & + (pci_resource_len(pdev, RNPM_NIC_BAR0) - 1)), + 1); +#endif + pf_adapter->hw_bar0 = hw_addr_bar0; + hw = &pf_adapter->hw; + + if (pci_resource_len(pdev, 0) == 8 * 1024 * 1024) + hw->rpu_addr = pf_adapter->hw_bar0; + else + hw->rpu_addr = NULL; + dbg("[bar0]:%p %llx len=%d MB rpu:%p\n", pf_adapter->hw_bar0, + (unsigned long long)pci_resource_start(pdev, 0), + (int)pci_resource_len(pdev, 0) / 1024 / 1024, hw->rpu_addr); + +#define RNPM_NIC_BAR4 (4) + pf_adapter->hw_addr4 = pf_adapter->hw_addr = + pcim_iomap(pdev, RNPM_NIC_BAR4, 0); + if (!pf_adapter->hw_addr) { + err = -EIO; + goto err_ioremap4; + } + + if (rnpm_is_pf1(pdev)) + pf_adapter->bd_number = pf0_cards_found++; + else + pf_adapter->bd_number = pf1_cards_found++; + mutex_init(&pf_adapter->mbx_lock); + + /* mailbox here */ + // hw->hw_addr = pf_adapter->hw_addr; +#ifdef FIX_VF_BUG + if (rnpm_is_pf1(pdev)) { + hw->hw_addr = pf_adapter->hw_addr4 + 0x100000; + pf_adapter->hw_addr = hw->hw_addr; + hw->ring_msix_base = hw->hw_addr + 0xa4000 + 0x200; + } else { + hw->hw_addr = pf_adapter->hw_addr; + hw->ring_msix_base = hw->hw_addr + 0xa4000; + } +#else + hw->hw_addr = pf_adapter->hw_addr; + hw->ring_msix_base = hw->hw_addr + 0xa4000; +#endif + + hw->pdev = pf_adapter->pdev; + hw->mbx.lock = &pf_adapter->mbx_lock; + rnpm_init_mbx_params_pf(hw); + memcpy(&hw->mbx.ops, ii->mbx_ops, sizeof(hw->mbx.ops)); +#ifdef NO_MBX_VERSION + /* in this mode; we set mode munaly */ + ii->mac = rnp_mac_n10g_x8_10G; + pf_adapter->adapter_cnt = ii->adapter_cnt; + if (rnpm_is_pf1(pdev)) { + pf_adapter->port_valid = port_valid_pf0; + pf_adapter->port_names = port_names_pf0; + } else { + pf_adapter->port_valid = port_valid_pf1; + pf_adapter->port_names = port_names_pf1; + } + // pf_adapter->hw.mac_type = ii->mac; + pf_adapter->hw.phy_type = PHY_TYPE_10G_BASE_SR; +#else + spin_lock_init(&pf_adapter->vlan_setup_lock); + spin_lock_init(&pf_adapter->drop_setup_lock); + spin_lock_init(&pf_adapter->dummy_setup_lock); + spin_lock_init(&pf_adapter->pf_setup_lock); + // hw->pf_setup_lock = &pf_adapter->pf_setup_lock; + /* setup priv_flags */ + spin_lock_init(&pf_adapter->priv_flags_lock); + + rnpm_mbx_pf_link_event_enable_nolock(hw, 0); + if (rnpm_mbx_get_capability(hw, ii)) { + dev_err(&pdev->dev, "rnp_mbx_get_capablity failed!\n"); + err = -EIO; + goto err_mbx_capability; + } + pf_adapter->port_valid = hw->lane_mask; + if (hw->port_ids != 0xffffffff) + pf_adapter->port_names = hw->port_ids; // port_names_pf0; + else + pf_adapter->port_names = port_names_pf0; + + pf_adapter->adapter_cnt = ii->adapter_cnt; + pf_adapter->hw.axi_mhz = hw->axi_mhz; + pf_adapter->hw.ncsi_en = hw->ncsi_en; + pf_adapter->hw.wol = hw->wol; +#endif + + /* some global var init here */ + spin_lock_init(&pf_adapter->key_setup_lock); + pf_adapter->default_rx_ring = 0; + spin_lock_init(&pf_adapter->mc_setup_lock); + + pf_adapter->mc_location = rnpm_mc_location_nic; + + // fixme n10 can get from device id vu440 cannot + // pf_adapter->board_type = MODE_TYPE; + // todo vu440 must decide mode_type + +/* vu440 can select board_type manul */ +#ifdef UV440_2PF + pf_adapter->board_type = MODE_TYPE; +#endif + switch (pf_adapter->hw.mode) { + case MODE_NIC_MODE_1PORT: + pf_adapter->mcft_size = 128; + break; + case MODE_NIC_MODE_2PORT: + case MODE_NIC_MODE_4PORT: + pf_adapter->mcft_size = 8; + break; + default: + pf_adapter->mcft_size = 128; + break; + } + + pf_adapter->mc_filter_type = rnpm_mc_filter_type0; + spin_lock_init(&pf_adapter->vlan_filter_lock); + + for (i = 0; i < MAX_PORT_NUM; i++) { + /* set this is true */ + pf_adapter->vlan_filter_status[i] = 1; + /* broadcast bypass should always set */ + pf_adapter->fctrl[i] = RNPM_FCTRL_BROADCASE_BYPASS; + } + pf_adapter->vlan_status_true = 1; + + pf_adapter->priv_flags = 0; +#ifdef FT_PADDING + rnpm_dbg("ft padding status on\n"); + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH; + data = rd32(pf_adapter, RNPM_DMA_CONFIG); + SET_BIT(padding_enable, data); + wr32(pf_adapter, RNPM_DMA_CONFIG, data); +#endif + + err = check_valid_mode(pf_adapter); + if (err) + goto err_msix; + + err = rnpm_init_msix_pf_adapter(pf_adapter); + + if (err) + goto err_msix; + + /* reset card */ + err = rnpm_reset_pf(pf_adapter); + if (err) + goto err_reset; + + err = rnpm_request_mbx_irq(pf_adapter); + if (err) + goto err_mbx_irq; + + /* setup rss key */ + // rnpm_init_rss_key(pf_adapter); + + /* tcam setup */ + // if (pf_adapter->adapter_cnt == 1) { + // wr32(pf_adapter, RNPM_ETH_TCAM_EN, 1); + // wr32(pf_adapter, RNPM_TOP_ETH_TCAM_CONFIG_ENABLE, 1); + // wr32(pf_adapter, RNPM_TCAM_MODE, 2); + // #define TCAM_NUM (4096) + // for (i = 0; i < TCAM_NUM; i++) { + // wr32(pf_adapter, RNPM_TCAM_SDPQF(i), 0); + // wr32(pf_adapter, RNPM_TCAM_DAQF(i), 0); + // wr32(pf_adapter, RNPM_TCAM_SAQF(i), 0); + // wr32(pf_adapter, RNPM_TCAM_APQF(i), 0); + // + // wr32(pf_adapter, RNPM_TCAM_SDPQF_MASK(i), 0); + // wr32(pf_adapter, RNPM_TCAM_DAQF_MASK(i), 0); + // wr32(pf_adapter, RNPM_TCAM_SAQF_MASK(i), 0); + // wr32(pf_adapter, RNPM_TCAM_APQF_MASK(i), 0); + // } + // wr32(pf_adapter, RNPM_TCAM_MODE, 1); + // } + // // should open all tx + // rnpm_fix_dma_tx_status(pf_adapter); + // should init timer service + timer_setup(&pf_adapter->service_timer, rnpm_pf_service_timer, 0); + INIT_WORK(&pf_adapter->service_task, rnpm_pf_service_task); + + return 0; +err_mbx_irq: + dev_err(&pdev->dev, "error: err_mbx_irq!\n"); + rnpm_rm_mbx_irq(pf_adapter); +err_reset: + dev_err(&pdev->dev, "error: err_reset!\n"); + rnpm_rm_mbx_irq(pf_adapter); + rnpm_rm_msix_pf_adapter(pf_adapter); + +err_msix: + dev_err(&pdev->dev, "error: err_msix!\n"); +err_mbx_capability: + pcim_iounmap(pdev, pf_adapter->hw_addr4); +err_ioremap0: +err_ioremap4: + devm_kfree(&pdev->dev, pf_adapter); + dev_err(&pdev->dev, "error: err_ioremap4!\n"); +err_pf_alloc: + dev_err(&pdev->dev, "error: err_pf_alloc!\n"); + return err; +} + +static int rnpm_rm_pf_adapter(struct pci_dev *pdev, + struct rnpm_pf_adapter **ppf_adapter) +{ + struct rnpm_pf_adapter *pf_adapter = *ppf_adapter; + + if (pf_adapter->service_timer.function) + del_timer_sync(&pf_adapter->service_timer); + cancel_work_sync(&pf_adapter->service_task); + + rnpm_rm_mbx_irq(*ppf_adapter); + rnpm_rm_msix_pf_adapter(*ppf_adapter); + + if (pf_adapter->rpu_inited) { + rnpm_rpu_mpe_stop(pf_adapter); + pf_adapter->rpu_inited = 0; + } + + if (pf_adapter->hw.ncsi_en) + rnpm_mbx_probe_stat_set(pf_adapter, MBX_REMOVE); + + rnpm_wr_reg(pf_adapter->hw_bar0 + + (0x7982fc & + (pci_resource_len(pdev, RNPM_NIC_BAR0) - 1)), + 1); + if (pf_adapter->hw_bar0) + pcim_iounmap(pdev, pf_adapter->hw_bar0); + if (pf_adapter->hw_addr4) + pcim_iounmap(pdev, pf_adapter->hw_addr4); + + if (pf_adapter) + devm_kfree(&pdev->dev, pf_adapter); + + return 0; +} + +static int rnpm_add_adpater(struct pci_dev *pdev, const struct rnpm_info *ii, + struct rnpm_adapter **padapter, + struct rnpm_pf_adapter *pf_adapter, int port, + int msix_offset, int port_name) +{ + int i, err = 0; + struct rnpm_adapter *adapter = NULL; + struct net_device *netdev; + struct rnpm_hw *hw; + unsigned int queues; + unsigned int indices; + int adapter_cnt = pf_adapter->adapter_cnt; + // netdev_features_t hw_enc_features = 0; +#ifndef NETIF_F_GSO_PARTIAL + netdev_features_t hw_features; +#endif /* NETIF_F_GSO_PARTIAL */ + queues = ii->total_queue_pair_cnts / adapter_cnt; + indices = queues; + pr_info("==== add adapter queues:%d table %d ===", queues, + pf_adapter->max_msix_counts[port]); + + netdev = alloc_etherdev_mq(sizeof(struct rnpm_adapter), indices); + if (!netdev) { + rnpm_err("alloc etherdev errors\n"); + return -ENOMEM; + } + + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + + adapter->bd_number = pf_adapter->bd_number; + adapter->port = port; + adapter->lane = port; + + adapter->max_ring_pair_counts = queues; + adapter->vector_off = msix_offset; + + adapter->max_msix_counts = pf_adapter->max_msix_counts[port]; + adapter->max_q_vectors = adapter->max_msix_counts; + + // todo maybe useful for not full ports valid in 8ports mode + adapter->layer2_count_max = ii->total_layer2_count / adapter_cnt; + adapter->layer2_offset = adapter->layer2_count_max * adapter->port; + adapter->tuple_5_count_max = ii->total_tuple5_count / adapter_cnt; + adapter->tuple_5_offset = adapter->tuple_5_count_max * adapter->port; + + adapter->priv_flags = pf_adapter->priv_flags; + +#ifdef RNPM_NAME_BY_LANES + snprintf(adapter->name, sizeof(netdev->name), "%s%ds%df%d", + rnpm_port_name, pdev->bus->number, rnpm_is_pf1(pdev), + adapter->port); +#else + snprintf(adapter->name, sizeof(netdev->name), "%s%ds%df%d", + rnpm_port_name, pdev->bus->number, rnpm_is_pf1(pdev), + port_name); +#endif + if (padapter) { + *padapter = adapter; + (*padapter)->pf_adapter = pf_adapter; + } + hw = &adapter->hw; + hw->back = adapter; + hw->nr_lane = hw->num = adapter->port; + hw->pdev = pdev; + hw->mode = pf_adapter->hw.mode; + hw->lane_mask = pf_adapter->hw.lane_mask; + hw->fw_version = pf_adapter->hw.fw_version; + hw->fw_uid = pf_adapter->hw.fw_uid; + // hw->mac_type = pf_adapter->hw.mac_type; + hw->phy.media_type = hw->phy_type = pf_adapter->hw.phy_type; + hw->axi_mhz = pf_adapter->hw.axi_mhz; + hw->is_sgmii = pf_adapter->hw.is_sgmii; + hw->phy.id = pf_adapter->hw.phy.id; + hw->single_lane_link_evt_ctrl_ablity = + pf_adapter->hw.single_lane_link_evt_ctrl_ablity; + hw->ncsi_rar_entries = pf_adapter->hw.ncsi_rar_entries; + hw->ncsi_en = pf_adapter->hw.ncsi_en; + hw->fw_lldp_ablity = pf_adapter->hw.fw_lldp_ablity; + hw->max_speed_1g = pf_adapter->hw.max_speed_1g; + adapter->wol = pf_adapter->hw.wol; + /* not so good ? */ + memcpy(&hw->mbx, &pf_adapter->hw.mbx, sizeof(pf_adapter->hw.mbx)); + memcpy(&hw->mac.ops, &pf_adapter->hw.mac.ops, + sizeof(pf_adapter->hw.mac.ops)); + + adapter->msg_enable = netif_msg_init(debug, NETIF_MSG_DRV +#ifdef MSG_PROBE_ENABLE + | NETIF_MSG_PROBE +#endif +#ifdef MSG_IFUP_ENABLE + | NETIF_MSG_IFUP +#endif +#ifdef MSG_IFDOWN_ENABLE + | NETIF_MSG_IFDOWN +#endif + ); + + if (rnpm_is_pf1(pdev)) + hw->pfvfnum = PF_NUM(1); + else + hw->pfvfnum = PF_NUM(0); + + /* adapter hw->mode to decide flags */ + switch (hw->mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + adapter->flags &= (~RNPM_FLAG_MUTIPORT_ENABLED); + break; + case MODE_NIC_MODE_2PORT: + adapter->flags |= RNPM_FLAG_MUTIPORT_ENABLED; + break; + case MODE_NIC_MODE_4PORT: + adapter->flags |= RNPM_FLAG_MUTIPORT_ENABLED; + break; + default: + adapter->flags |= RNPM_FLAG_MUTIPORT_ENABLED; + break; + } + + /* this is relative with netdev name */ + /* in mutiport mode not support this */ + // if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) { +#if !defined(NO_ETHDEV_PORT) + if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) + netdev->dev_port = port_name; + + SET_NETDEV_DEV(netdev, &pdev->dev); +#endif + + adapter->portid_of_card = port_name; + //} + /* no use now */ + hw->default_rx_queue = 0; + + hw->rss_type = ii->rss_type; +#ifdef FIX_VF_BUG + if (hw->pfvfnum) { + hw->hw_addr = pf_adapter->hw_addr4 + 0x100000; + pf_adapter->hw_addr = hw->hw_addr; + + hw->ring_msix_base = hw->hw_addr + 0xa4000 + 0x200; + } else { + hw->hw_addr = pf_adapter->hw_addr; + hw->ring_msix_base = hw->hw_addr + 0xa4000; + } +#else + hw->hw_addr = pf_adapter->hw_addr; + hw->ring_msix_base = hw->hw_addr + 0xa4000; +#endif + hw->rpu_addr = pf_adapter->hw.rpu_addr; + + rnpm_fix_queue_number(hw); + /* get version */ + hw->dma_version = rd32(hw, RNPM_DMA_VERSION); + pr_info("%s %s: dma version:0x%x, nic version:0x%x, pfvfnum:0x%x lane%d %p\n", + adapter->name, pci_name(pdev), hw->dma_version, + rd32(hw, RNPM_TOP_NIC_VERSION), hw->pfvfnum, hw->nr_lane, hw); + + rnpm_assign_netdev_ops(netdev); + strscpy(netdev->name, adapter->name, sizeof(netdev->name)); + + /* Setup hw api */ + memcpy(&hw->mac.ops, ii->mac_ops, sizeof(hw->mac.ops)); + // hw->mac.type = ii->mac; + + /* PHY */ + memcpy(&hw->phy.ops, ii->phy_ops, sizeof(hw->phy.ops)); + hw->phy.sfp_type = rnpm_sfp_type_unknown; + + /* PCS */ + memcpy(&hw->pcs.ops, ii->pcs_ops, sizeof(hw->pcs.ops)); + + + ii->get_invariants(hw); + /* setup the private structure */ + /* this private is used only once */ + err = rnpm_sw_init(adapter); + if (err) { + err = -EIO; + goto err_sw_init; + } + + /* Cache if MNG FW is up so we don't have to read the REG later */ + if (hw->mac.ops.mng_fw_enabled) + hw->mng_fw_enabled = hw->mac.ops.mng_fw_enabled(hw); + + hw->phy.reset_if_overtemp = false; + /* reset_hw fills in the perm_addr as well */ + err = hw->mac.ops.reset_hw(hw); + if (err) { + e_dev_err("HW Init failed: %d\n", err); + err = -EIO; + goto err_sw_init; + } + + /* MTU range: 68 - 9710 */ + netdev->min_mtu = RNPM_MIN_MTU; + netdev->max_mtu = + RNPM_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + 2 * ETH_FCS_LEN); +#ifdef NETIF_F_GSO_PARTIAL + + if (hw->feature_flags & RNPM_NET_FEATURE_SG) + netdev->features |= NETIF_F_SG; + if (hw->feature_flags & RNPM_NET_FEATURE_TSO) + netdev->features |= NETIF_F_TSO | NETIF_F_TSO6; + if (hw->feature_flags & RNPM_NET_FEATURE_RX_HASH) + netdev->features |= NETIF_F_RXHASH; + if (hw->feature_flags & RNPM_NET_FEATURE_RX_CHECKSUM) + netdev->features |= NETIF_F_RXCSUM; + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + netdev->features |= NETIF_F_HW_CSUM | NETIF_F_SCTP_CRC; + + netdev->features |= NETIF_F_HIGHDMA; + netdev->gso_partial_features = RNPM_GSO_PARTIAL_FEATURES; + netdev->features |= NETIF_F_GSO_PARTIAL | RNPM_GSO_PARTIAL_FEATURES; + + netdev->hw_features |= netdev->features; + + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_FILTER) + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_OFFLOAD) { + netdev->hw_features |= + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + } + netdev->hw_features |= NETIF_F_RXALL; + if (hw->feature_flags & RNPM_NET_FEATURE_RX_NTUPLE_FILTER) + netdev->hw_features |= NETIF_F_NTUPLE; + if (hw->feature_flags & RNPM_NET_FEATURE_RX_FCS) + netdev->hw_features |= NETIF_F_RXFCS; + + netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; + netdev->hw_enc_features |= netdev->vlan_features; + netdev->mpls_features |= NETIF_F_HW_CSUM; + + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_FILTER) + netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_OFFLOAD) { + netdev->features |= + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + } + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->priv_flags |= IFF_SUPP_NOFCS; + + if (adapter->flags2 & RNPM_FLAG2_RSC_CAPABLE) + netdev->hw_features |= NETIF_F_LRO; + +#else /* NETIF_F_GSO_PARTIAL */ + + if (hw->feature_flags & RNPM_NET_FEATURE_SG) + netdev->features |= NETIF_F_SG; + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + netdev->features |= NETIF_F_IP_CSUM; + + netdev->features |= NETIF_F_HIGHDMA; + + netdev->features |= + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM; + +#ifdef NETIF_F_IPV6_CSUM + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + netdev->features |= NETIF_F_IPV6_CSUM; +#endif /* NETIF_F_IPV6_CSUM */ + +#ifdef NETIF_F_HW_VLAN_CTAG_TX + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_FILTER) + netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (hw->feature_flags & RNPM_NET_FEATURE_VLAN_OFFLOAD) { + netdev->features |= + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + } +#endif /* NETIF_F_HW_VLAN_CTAG_TX */ + netdev->features |= rnpm_tso_features(); + +#ifdef NETIF_F_RXHASH + if (hw->feature_flags & RNPM_NET_FEATURE_RX_HASH) + netdev->features |= NETIF_F_RXHASH; +#endif /* NETIF_F_RXHASH */ + + if (hw->feature_flags & RNPM_NET_FEATURE_RX_CHECKSUM) + netdev->features |= NETIF_F_RXCSUM; + /* copy netdev features into list of user selectable features */ + hw_features = netdev->hw_features; + hw_features |= netdev->features; + + /* give us the option of enabling RSC/LRO later */ + if (adapter->flags2 & RNPM_FLAG2_RSC_CAPABLE) + hw_features |= NETIF_F_LRO; + + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + hw_features |= NETIF_F_SCTP_CSUM; + if (hw->feature_flags & RNPM_NET_FEATURE_RX_NTUPLE_FILTER) + hw_features |= NETIF_F_NTUPLE; + + hw_features |= NETIF_F_RXALL; + + if (hw->feature_flags & RNPM_NET_FEATURE_RX_FCS) + hw_features |= NETIF_F_RXFCS; + + netdev->hw_features = hw_features; + if (hw->feature_flags & RNPM_NET_FEATURE_SG) + netdev->vlan_features |= NETIF_F_SG; + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + netdev->vlan_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + if (hw->feature_flags & RNPM_NET_FEATURE_TSO) + netdev->vlan_features |= NETIF_F_TSO | NETIF_F_TSO6; + + netdev->hw_enc_features |= NETIF_F_SG; + + if (hw->feature_flags & RNPM_NET_FEATURE_TX_CHECKSUM) + netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + +#endif /* NETIF_F_GSO_PARTIAL */ + +#ifdef IFF_UNICAST_FLT + netdev->priv_flags |= IFF_UNICAST_FLT; +#endif /* IFF_UNICAST_FLT */ +#ifdef IFF_SUPP_NOFCS + netdev->priv_flags |= IFF_SUPP_NOFCS; +#endif /* IFF_SUPP_NOFCS */ + +#ifdef NET_FEATURE_DCB +#ifdef CONFIG_DCB + rnpm_dcb_init(netdev, adapter); +#endif /* CONFIG_DCB */ +#endif /* NET_FEATURE_DCB */ + + if (adapter->flags2 & RNPM_FLAG2_RSC_ENABLED) + netdev->features |= NETIF_F_LRO; + eth_hw_addr_set(netdev, hw->mac.perm_addr); +#ifdef ETHTOOL_GPERMADDR + memcpy(netdev->perm_addr, hw->mac.perm_addr, netdev->addr_len); +#endif + pr_info("set dev_addr:%pM\n", netdev->dev_addr); + + if (!is_valid_ether_addr(netdev->dev_addr)) { + e_dev_err("invalid MAC address\n"); + err = -EIO; + /* handle error not corect */ + goto err_sw_init; + } + ether_addr_copy(hw->mac.addr, hw->mac.perm_addr); + + timer_setup(&adapter->service_timer, rnpm_service_timer, 0); + +#ifndef NO_PTP + if (module_enable_ptp) { + adapter->flags2 |= RNPM_FLAG2_PTP_ENABLED; + if (adapter->flags2 & RNPM_FLAG2_PTP_ENABLED) { + adapter->tx_timeout_factor = 10; + INIT_WORK(&adapter->tx_hwtstamp_work, + rnpm_tx_hwtstamp_work); + } + } +#endif + + INIT_WORK(&adapter->service_task, rnpm_service_task); + clear_bit(__RNPM_SERVICE_SCHED, &adapter->state); + + err = rnpm_init_interrupt_scheme(adapter); + if (err) { + err = -EIO; + goto err_interrupt_scheme; + } + + /* reset the hardware with the new settings */ + err = hw->mac.ops.start_hw(hw); + + adapter->pf_adapter->force_10g_1g_speed_ablity = + rnpm_is_pf1(pdev) ? !!force_speed_ablity_pf1 : + !!force_speed_ablity_pf0; + if (hw->max_speed_1g) + adapter->pf_adapter->force_10g_1g_speed_ablity = 0; + + if (adapter->pf_adapter->force_10g_1g_speed_ablity) + pf_adapter->priv_flags |= RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY; + + /* Disable fiber force speed */ + if (hw->max_speed_1g == 0) + rnpm_mbx_force_speed(hw, 0); + + strscpy(netdev->name, adapter->name, sizeof(netdev->name)); + + if (fix_eth_name) { + if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) { + snprintf(adapter->name, sizeof(netdev->name), "rnp%d%d", + rnpm_is_pf1(pdev), adapter->bd_number); + } else { + snprintf(adapter->name, sizeof(netdev->name), + "rnpm%d%d%d", rnpm_is_pf1(pdev), + adapter->bd_number, adapter->port); + } + strscpy(netdev->name, adapter->name, sizeof(netdev->name)); + } else { +#ifdef ASSIN_PDEV + strscpy(netdev->name, "eth%d", sizeof(netdev->name)); +#else + if (!(adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED)) + strscpy(netdev->name, "eth%d", sizeof(netdev->name)); + +#endif + /* multiports we can't support eth%d */ + } + + err = register_netdev(netdev); + if (err) { + err = -EIO; + rnpm_err("register_netdev failed! err code %x\n", err); + goto err_register; + } + adapter->netdev_registered = true; + + /* power down the optics for n10 SFP+ fiber */ + if (hw->mac.ops.disable_tx_laser) + hw->mac.ops.disable_tx_laser(hw); + + if (hw->ncsi_en) + control_mac_rx(adapter, true); + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { + e_info(probe, "IOV is enabled with %d VFs\n", adapter->num_vfs); + for (i = 0; i < adapter->num_vfs; i++) + rnpm_vf_configuration(pdev, (i | 0x10000000)); + } + + if (rnpm_mbx_lldp_status_get(hw) == 1) + adapter->priv_flags |= RNPM_PRIV_FLAG_LLDP_EN_STAT; + + if (rnpm_sysfs_init(adapter, port)) + e_err(probe, "failed to allocate sysfs resources\n"); + + rnpm_dbg_adapter_init(adapter); + + /* Need link setup for MNG FW, else wait for RNPM_UP */ + // if (hw->mng_fw_enabled && hw->mac.ops.setup_link) + // hw->mac.ops.setup_link(hw, RNPM_LINK_SPEED_10GB_FULL | + // RNPM_LINK_SPEED_1GB_FULL, true); + + return 0; + // e_dev_err("error: unregister_netdev\n"); + // unregister_netdev(netdev); + +err_register: + e_dev_err("error: err_register err=%d\n", err); + rnpm_clear_interrupt_scheme(adapter); +err_interrupt_scheme: + e_dev_err("error: err_interrupt_scheme err=%d\n", err); + if (adapter->service_timer.function) + del_timer_sync(&adapter->service_timer); +err_sw_init: + e_dev_err("error: err_sw_init err=%d\n", err); + /* cannot handle right */ + rnpm_disable_sriov(adapter); + adapter->flags2 &= ~RNPM_FLAG2_SEARCH_FOR_SFP; + // err_ioremap: + free_netdev(netdev); + adapter->netdev_registered = false; + + return err; +} + +int rnpm_can_rpu_start(struct rnpm_pf_adapter *pf_adapter) +{ + if (pf_adapter->hw.rpu_addr == NULL) + return 0; + if ((pf_adapter->pdev->device & 0xff00) == 0x1c00) + return 1; + if (pf_adapter->hw.rpu_availble) + return 1; + return 0; +} + +/** + * rnpm_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in rnpm_pci_tbl + * + * Returns 0 on success, negative on failure + * + * rnpm_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int rnpm_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + // struct net_device *netdev; + // struct rnpm_adapter *adapter; + struct rnpm_pf_adapter *pf_adapter; + const struct rnpm_info *ii; + int i = 0, vector_idx = 0, err; + int vector_idx_new, port_name, port_name_new, lane_num; + int valid_port; + u32 port_valid; + + /* Catch broken hardware that put the wrong VF device ID in + * the PCIe SR-IOV capability. + */ + if (pdev->is_virtfn) { + WARN(1, "%s (%x:%x) should not be a VF!\n", pci_name(pdev), + pdev->vendor, pdev->device); + return -EINVAL; + } +#ifdef HAVE_PCI_DEV_FLAGS_NO_BUS_RESET + /*not support bus reset*/ + pdev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET; +#endif + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(&pdev->dev, "pci_enable_device_mem failed 0x%x\n", err); + return err; + } + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(56)) && + !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56))) { + enable_hi_dma = 1; + } else { + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) { + err = dma_set_coherent_mask(&pdev->dev, + DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; + } + } + enable_hi_dma = 0; + } + // err = pci_request_selected_regions(pdev, pci_select_bars(pdev, + // IORESOURCE_MEM), rnpm_driver_name); + err = pci_request_mem_regions(pdev, rnpm_driver_name); + if (err) { + dev_err(&pdev->dev, + "pci_request_selected_regions failed 0x%x\n", err); + goto err_pci_reg; + } + pci_set_master(pdev); + pci_save_state(pdev); + + err = rnpm_add_pf_adapter(pdev, &pf_adapter, id); + if (err) { + dev_err(&pdev->dev, "rnpm_add_pf_adapter failed 0x%x\n", err); + goto err_pf_adpater; + } + + // only pf0 download mpe + if ((rnpm_is_pf1(pf_adapter->pdev) == 0) && + rnpm_can_rpu_start(pf_adapter)) { + rnpm_rpu_mpe_start(pf_adapter); + } + ii = rnpm_info_tbl[pf_adapter->board_type]; + // pf_adapter->adapter_cnt = ii->adapter_cnt; + memset(pf_adapter->adapter, 0, sizeof(pf_adapter->adapter)); + if (pf_adapter->adapter_cnt > MAX_PORT_NUM) { + dev_err(&pdev->dev, "invalid adapt cnt:%d\n", + pf_adapter->adapter_cnt); + return -EIO; + } + valid_port = Hamming_weight_1(pf_adapter->port_valid); + port_valid = pf_adapter->port_valid; + do { + port_name = -1; + vector_idx = 1; + lane_num = 0; + vector_idx_new = 1; + // get the min port name + for (i = 0, vector_idx = 1; i < pf_adapter->adapter_cnt; i++) { + if (port_valid & (1 << i)) { + port_name_new = + (pf_adapter->port_names >> (i * 8)) & + 0xff; + if ((port_name == -1) || + (port_name > port_name_new)) { + // get the current port name + port_name = port_name_new; + lane_num = i; + vector_idx_new = vector_idx; + } + } + vector_idx += pf_adapter->max_msix_counts[i]; + } + // do register + err = rnpm_add_adpater(pdev, ii, &pf_adapter->adapter[lane_num], + pf_adapter, lane_num, vector_idx_new, + port_name); + if (err) { + dev_err(&pdev->dev, "add adpater %d failed, err=%d\n", + i, err); + goto err_adpater; + } + + // mask valid + port_valid &= (~(1 << lane_num)); + valid_port--; + + } while (valid_port > 0); + + // wr32(&pf_adapter->hw, RNPM_ETH_EXCEPT_DROP_PROC, 0xf); + if (rnpm_card_partially_supported_10g_1g_sfp(pf_adapter)) { + if (fw_10g_1g_auto_det) + pf_adapter->priv_flags |= + RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN; + rnpm_hw_set_fw_10g_1g_auto_detch(&pf_adapter->hw, + fw_10g_1g_auto_det); + } + +#ifndef NO_MBX_VERSION + if (pf_adapter->hw.single_lane_link_evt_ctrl_ablity == 0) + rnpm_mbx_pf_link_event_enable_nolock(&pf_adapter->hw, 1); +#endif + mod_timer(&pf_adapter->service_timer, HZ + jiffies); + if (pf_adapter->hw.ncsi_en) + rnpm_mbx_probe_stat_set(pf_adapter, MBX_PROBE); + + return 0; + +err_adpater: + dev_err(&pdev->dev, "error: err_adpater!\n"); + rnpm_rm_pf_adapter(pdev, &pf_adapter); +err_pf_adpater: + pci_release_mem_regions(pdev); +err_dma: +err_pci_reg: + dev_err(&pdev->dev, "probe err = %d!\n", err); + return err; +} + +/** + * rnpm_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * rnpm_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void rnpm_remove(struct pci_dev *pdev) +{ + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(pdev); + int i; + + set_bit(__RNPM_DOWN, &pf_adapter->state); + + /* Disable fw send link event to rc */ + rnpm_mbx_pf_link_event_enable_nolock(&pf_adapter->hw, 0); + + while (test_and_set_bit(__RNPM_RESETTING, &pf_adapter->state)) + usleep_range(1000, 2000); + + while (mutex_lock_interruptible(pf_adapter->hw.mbx.lock)) + usleep_range(1000, 2000); + set_bit(__RNPM_REMOVING, &pf_adapter->state); + mutex_unlock(pf_adapter->hw.mbx.lock); + + /* must rm in this order */ + for (i = pf_adapter->adapter_cnt - 1; i >= 0; i--) { + if (rnpm_port_is_valid(pf_adapter, i)) { + if (pf_adapter->adapter[i]) + rnpm_rm_adpater(pf_adapter->adapter[i]); + } + } + + // disable mbx-irq + if (pf_adapter->hw.mbx.ops.configure) + pf_adapter->hw.mbx.ops.configure(&pf_adapter->hw, 0, false); + + rnpm_rm_pf_adapter(pdev, &pf_adapter); + // pci_release_selected_regions(pdev, pci_select_bars(pdev, + // IORESOURCE_MEM)); + dma_free_coherent(&pdev->dev, pf_adapter->hw.mbx.reply_dma_size, + pf_adapter->hw.mbx.reply_dma, + pf_adapter->hw.mbx.reply_dma_phy); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + +/** + * rnpm_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t rnpm_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * rnpm_io_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. + */ +static pci_ers_result_t rnpm_io_slot_reset(struct pci_dev *pdev) +{ + pci_ers_result_t result = PCI_ERS_RESULT_NONE; + + return result; +} + +#ifdef CONFIG_PM +static int rnpm_resume(struct pci_dev *pdev) +{ + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(pdev); + struct rnpm_adapter *adapter; + struct net_device *netdev; + int i; + u32 err; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + /* pci_restore_state clears dev->state_saved so call + * pci_save_state to restore it. + */ + pci_save_state(pdev); + + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); + return err; + } + + pci_set_master(pdev); + + pci_wake_from_d3(pdev, false); + + err = rnpm_init_msix_pf_adapter(pf_adapter); + + rnpm_request_mbx_irq(pf_adapter); + + if (pf_adapter->adapter_cnt == 1) { + wr32(pf_adapter, RNPM_ETH_TCAM_EN, 1); + wr32(pf_adapter, RNPM_TOP_ETH_TCAM_CONFIG_ENABLE, 1); + wr32(pf_adapter, RNPM_TCAM_MODE, 2); +#define TCAM_NUM (4096) + for (i = 0; i < TCAM_NUM; i++) { + wr32(pf_adapter, RNPM_TCAM_SDPQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_DAQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_SAQF(i), 0); + wr32(pf_adapter, RNPM_TCAM_APQF(i), 0); + + wr32(pf_adapter, RNPM_TCAM_SDPQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_DAQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_SAQF_MASK(i), 0); + wr32(pf_adapter, RNPM_TCAM_APQF_MASK(i), 0); + } + wr32(pf_adapter, RNPM_TCAM_MODE, 1); + } + + // should open all tx + rnpm_fix_dma_tx_status(pf_adapter); + + for (i = 0; i < pf_adapter->adapter_cnt; i++) { + if (!rnpm_port_is_valid(pf_adapter, i)) + continue; + + adapter = pf_adapter->adapter[i]; + netdev = adapter->netdev; + rnpm_reset(adapter); + rtnl_lock(); + err = rnpm_init_interrupt_scheme(adapter); + if (!err && netif_running(netdev)) + err = rnpm_open(netdev); + + rtnl_unlock(); + netif_device_attach(netdev); + } + + // RNPM_WRITE_REG(&adapter->hw, RNPM_WUS, ~0); + + if (err) + return err; + + return 0; +} +#endif /* CONFIG_PM */ + +__maybe_unused static int __rnpm_shutdown(struct pci_dev *pdev, + bool *enable_wake) +{ + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(pdev); + struct rnpm_adapter *adapter; + int i; + struct net_device *netdev; + struct rnpm_hw *hw; + u32 wufc = 0; +#ifdef CONFIG_PM + int retval = 0; +#endif + + for (i = pf_adapter->adapter_cnt - 1; i >= 0; i--) { + if (!rnpm_port_is_valid(pf_adapter, i)) + continue; + + adapter = pf_adapter->adapter[i]; + netdev = adapter->netdev; + hw = &adapter->hw; + rtnl_lock(); + netif_device_detach(netdev); + if (netif_running(netdev)) { + rnpm_down(adapter); + rnpm_free_irq(adapter); + rnpm_free_all_tx_resources(adapter); + rnpm_free_all_rx_resources(adapter); + } + rtnl_unlock(); + /* free msix */ + // adapter->rm_mode = true; + rnpm_clear_interrupt_scheme(adapter); + + // wufc |= adapter->wol; + wufc = adapter->wol; + if (wufc) { + rnpm_set_rx_mode(netdev); + + /* enable the optics for n10 SFP+ fiber as we can WoL */ + if (hw->mac.ops.enable_tx_laser) + hw->mac.ops.enable_tx_laser(hw); + + /* turn on all-multi mode if wake on multicast is enabled */ + + } else { + } + } + +#ifdef CONFIG_PM + retval = pci_save_state(pdev); + if (retval) + return retval; + +#endif + + pci_wake_from_d3(pdev, false); + *enable_wake = false; + rnpm_rm_mbx_irq(pf_adapter); + rnpm_rm_msix_pf_adapter(pf_adapter); + + pci_disable_device(pdev); + + return 0; +} + +#ifdef CONFIG_PM +static int rnpm_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int retval; + bool wake; + + retval = __rnpm_shutdown(pdev, &wake); + if (retval) + return retval; + + if (wake) { + pci_prepare_to_sleep(pdev); + } else { + pci_wake_from_d3(pdev, false); + pci_set_power_state(pdev, PCI_D3hot); + } + + return 0; +} +#endif /* CONFIG_PM */ + +__maybe_unused static void rnpm_shutdown(struct pci_dev *pdev) +{ + bool wake; + + __rnpm_shutdown(pdev, &wake); + + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, wake); + pci_set_power_state(pdev, PCI_D3hot); + } +} + +/** + * rnpm_io_resume - called when traffic can start flowing again. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. + */ +static void rnpm_io_resume(struct pci_dev *pdev) +{ + struct rnpm_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev = adapter->netdev; + +#ifdef CONFIG_PCI_IOV + if (adapter->vferr_refcount) { + e_info(drv, "Resuming after VF err\n"); + adapter->vferr_refcount--; + return; + } + +#endif + if (netif_running(netdev)) + rnpm_up(adapter); + + netif_device_attach(netdev); +} + +static const struct pci_error_handlers rnpm_err_handler = { + .error_detected = rnpm_io_error_detected, + .slot_reset = rnpm_io_slot_reset, + .resume = rnpm_io_resume, +}; + +static struct pci_driver rnpm_driver = { + .name = rnpm_driver_name, + .id_table = rnpm_pci_tbl, + .probe = rnpm_probe, + .remove = rnpm_remove, +#ifdef CONFIG_PM + .suspend = rnpm_suspend, + .resume = rnpm_resume, +#endif + //.shutdown = rnpm_shutdown, + // .sriov_configure = rnpm_pci_sriov_configure, + .err_handler = &rnpm_err_handler +}; + +static int __init rnpm_init_module(void) +{ + int ret; + + pr_info("%s - version %s\n", rnpm_driver_string, rnpm_driver_version); + pr_info("%s\n", rnpm_copyright); + rnpm_dbg_init(); + ret = pci_register_driver(&rnpm_driver); + if (ret) { + rnpm_dbg_exit(); + return ret; + } + + return 0; +} +module_init(rnpm_init_module); + +static void __exit rnpm_exit_module(void) +{ + pci_unregister_driver(&rnpm_driver); + + rnpm_dbg_exit(); + + rcu_barrier(); /* Wait for completion of call_rcu()'s */ +} + +module_exit(rnpm_exit_module); diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.c b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.c new file mode 100644 index 000000000000..950fbc8b60cc --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include "rnpm.h" +#include "rnpm_type.h" +#include "rnpm_common.h" +#include "rnpm_mbx.h" +#include "rnpm_mbx_fw.h" + +// #define MBX_RD_DEBUG + +/* == VEC == */ +#define VF2PF_MBOX_VEC(VF) (0xa5100 + 4 * (VF)) +#define CPU2PF_MBOX_VEC (0xa5300) + +/* == PF <--> VF mailbox ==== */ +/* 64bytes */ +#define SHARE_MEM_BYTES 64 +/* for PF1 rtl will remap 6000 to 0xb000 */ +#define PF_VF_SHM(vf) ((0xa6000) + (64 * (vf))) +#define PF2VF_COUNTER(vf) (PF_VF_SHM(vf) + 0) +#define VF2PF_COUNTER(vf) (PF_VF_SHM(vf) + 4) +#define PF_VF_SHM_DATA(vf) (PF_VF_SHM(vf) + 8) +#define PF2VF_MBOX_CTRL(vf) ((0xa7100) + (4 * (vf))) +#define PF_VF_MBOX_MASK_LO ((0xa7200)) +#define PF_VF_MBOX_MASK_HI ((0xa7300)) + +//=== CPU <--> PF === +#define CPU_PF_SHM (0xaa000) +#define CPU2PF_COUNTER (CPU_PF_SHM + 0) +#define PF2CPU_COUNTER (CPU_PF_SHM + 4) +#define CPU_PF_SHM_DATA (CPU_PF_SHM + 8) +#define PF2CPU_MBOX_CTRL (0xaa100) +#define CPU_PF_MBOX_MASK (0xaa300) + +#define MBOX_CTRL_REQ (1 << 0) // WO +//#define MBOX_CTRL_VF_HOLD_SHM (1<<2) // VF:WR, PF:RO +#define MBOX_CTRL_PF_HOLD_SHM (1 << 3) // VF:RO, PF:WR +//#define MBOX_CTRL_PF_CPU_HOLD_SHM (1<<3) // for pf <--> cpu + +#define MBOX_IRQ_EN 0 +#define MBOX_IRQ_DISABLE 1 + +#define mbx_prd32(hw, reg) prnp_rd_reg((hw)->hw_addr + (reg)) +#define mbx_rd32(hw, reg) rnpm_rd_reg((hw)->hw_addr + (reg)) +#define mbx_pwr32(hw, reg, val) p_rnp_wr_reg((hw)->hw_addr + (reg), (val)) +#define mbx_wr32(hw, reg, val) rnpm_wr_reg((hw)->hw_addr + (reg), (val)) + +/** + * rnpm_read_mbx - Reads a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox/vfnum to read + * + * returns SUCCESS if it successfully read message from buffer + **/ +s32 rnpm_read_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = RNPM_ERR_MBX; + + /* limit read to size of mailbox */ + if (size > mbx->size) + size = mbx->size; + + if (mbx->ops.read) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); + else + TRACE(); + + return ret_val; +} + +/** + * rnpm_write_mbx - Write a message to the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +s32 rnpm_write_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = 0; + + if (size > mbx->size) + ret_val = RNPM_ERR_MBX; + else if (mbx->ops.write) + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + return ret_val; +} + +static inline u16 rnpm_mbx_get_req(struct rnpm_hw *hw, int reg) +{ + /* memory barrior */ + mb(); + return mbx_rd32(hw, reg) & 0xffff; +} + +static inline u16 rnpm_mbx_get_ack(struct rnpm_hw *hw, int reg) +{ + /* memory barrior */ + mb(); + return (mbx_rd32(hw, reg) >> 16) & 0xffff; +} + +static inline void rnpm_mbx_inc_pf_req(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + u16 req; + int reg = + (mbx_id == MBX_CM3CPU) ? PF2CPU_COUNTER : PF2VF_COUNTER(mbx_id); + u32 v = mbx_rd32(hw, reg); + + req = (v & 0xffff); + req++; + v &= ~(0x0000ffff); + v |= req; + /* memory barrior */ + mb(); + + mbx_wr32(hw, reg, v); + + /* update stats */ + hw->mbx.stats.msgs_tx++; +} + +static inline void rnpm_mbx_inc_pf_ack(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + u16 ack; + int reg = + (mbx_id == MBX_CM3CPU) ? PF2CPU_COUNTER : PF2VF_COUNTER(mbx_id); + u32 v = mbx_rd32(hw, reg); + + ack = (v >> 16) & 0xffff; + ack++; + v &= ~(0xffff0000); + v |= (ack << 16); + /* memory barrior */ + mb(); + mbx_wr32(hw, reg, v); + + /* update stats */ + hw->mbx.stats.msgs_rx++; +} + +/** + * rnpm_check_for_msg - checks to see if someone sent us mail + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 rnpm_check_for_msg(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = RNPM_ERR_MBX; + + if (mbx->ops.check_for_msg) + ret_val = mbx->ops.check_for_msg(hw, mbx_id); + + return ret_val; +} + +/** + * rnpm_check_for_ack - checks to see if someone sent us ACK + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 rnpm_check_for_ack(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = RNPM_ERR_MBX; + + if (mbx->ops.check_for_ack) + ret_val = mbx->ops.check_for_ack(hw, mbx_id); + + return ret_val; +} + +/** + * rnpm_poll_for_msg - Wait for message notification + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification + **/ +static s32 rnpm_poll_for_msg(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + if (!countdown || !mbx->ops.check_for_msg) + goto out; + + while (countdown && mbx->ops.check_for_msg(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + udelay(mbx->usec_delay); + } + +out: + return countdown ? 0 : -ETIME; +} + +/** + * rnpm_poll_for_ack - Wait for message acknowledgment + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message acknowledgment + **/ +static s32 rnpm_poll_for_ack(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + if (!countdown || !mbx->ops.check_for_ack) + goto out; + + while (countdown && mbx->ops.check_for_ack(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + udelay(mbx->usec_delay); + } + +out: + return countdown ? 0 : -ETIME; +} + +/** + * rnpm_read_posted_mbx - Wait for message notification and receive message + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification and + * copied it into the receive buffer. + **/ +static s32 rnpm_read_posted_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, + enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = RNPM_ERR_MBX; + + if (!mbx->ops.read) + goto out; + + ret_val = rnpm_poll_for_msg(hw, mbx_id); + + /* if ack received read message, otherwise we timed out */ + if (!ret_val) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); +out: + return ret_val; +} + +/** + * rnpm_write_posted_mbx - Write a message to the mailbox, wait for ack + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer and + * received an ack to that message within delay * timeout period + **/ +static s32 rnpm_write_posted_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, + enum MBX_ID mbx_id) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + s32 ret_val = RNPM_ERR_MBX; + + /* exit if either we can't write or there isn't a defined timeout */ + if (!mbx->ops.write || !mbx->timeout) + goto out; + + /* send msg and hold buffer lock */ + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + /* if msg sent wait until we receive an ack */ + if (!ret_val) + ret_val = rnpm_poll_for_ack(hw, mbx_id); + +out: + return ret_val; +} + +/** + * rnpm_check_for_msg_pf - checks to see if the VF has sent mail + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 rnpm_check_for_msg_pf(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + s32 ret_val = RNPM_ERR_MBX; + + if (mbx_id == MBX_CM3CPU) { + if (rnpm_mbx_get_req(hw, CPU2PF_COUNTER) != hw->mbx.cpu_req) { + ret_val = 0; + hw->mbx.stats.reqs++; + } + } else { + if (rnpm_mbx_get_req(hw, VF2PF_COUNTER(mbx_id)) != + hw->mbx.vf_req[mbx_id]) { + ret_val = 0; + hw->mbx.stats.reqs++; + } + } + + return ret_val; +} + +/** + * rnpm_check_for_ack_pf - checks to see if the VF has ACKed + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 rnpm_check_for_ack_pf(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + s32 ret_val = RNPM_ERR_MBX; + + if (mbx_id == MBX_CM3CPU) { + if (rnpm_mbx_get_ack(hw, CPU2PF_COUNTER) != hw->mbx.cpu_ack) { + ret_val = 0; + hw->mbx.stats.acks++; + } + } else { + if (rnpm_mbx_get_ack(hw, VF2PF_COUNTER(mbx_id)) != + hw->mbx.vf_ack[mbx_id]) { + ret_val = 0; + hw->mbx.stats.acks++; + } + } + + return ret_val; +} + +/** + * rnpm_obtain_mbx_lock_pf - obtain mailbox lock + * @hw: pointer to the HW structure + * @mbx_id: the VF index or CPU + * + * return SUCCESS if we obtained the mailbox lock + **/ +static s32 rnpm_obtain_mbx_lock_pf(struct rnpm_hw *hw, enum MBX_ID mbx_id) +{ + int try_cnt = 5000; + s32 ret_val = -EPERM; + u32 CTRL_REG = (mbx_id == MBX_CM3CPU) ? PF2CPU_MBOX_CTRL : + PF2VF_MBOX_CTRL(mbx_id); + + while (try_cnt-- > 0) { + /* Take ownership of the buffer */ + mbx_wr32(hw, CTRL_REG, MBOX_CTRL_PF_HOLD_SHM); + /* memory barrior */ + mb(); + /* reserve mailbox for cm3 use */ + if (mbx_rd32(hw, CTRL_REG) & MBOX_CTRL_PF_HOLD_SHM) + return 0; + udelay(100); + } + + rnpm_err("%s: failed to get:%d lock\n", __func__, mbx_id); + return ret_val; +} + +/** + * rnpm_write_mbx_pf - Places a message in the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: the VF index + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +static s32 rnpm_write_mbx_pf(struct rnpm_hw *hw, u32 *msg, u16 size, + enum MBX_ID mbx_id) +{ + s32 ret_val = 0; + u16 i; + u32 DATA_REG = (mbx_id == MBX_CM3CPU) ? CPU_PF_SHM_DATA : + PF_VF_SHM_DATA(mbx_id); + u32 CTRL_REG = (mbx_id == MBX_CM3CPU) ? PF2CPU_MBOX_CTRL : + PF2VF_MBOX_CTRL(mbx_id); + u32 wait_msg_free_cnt = 4; + + if (size > RNPM_VFMAILBOX_SIZE) { + rnpm_info("%s: size:%d should <%d\n", __func__, size, + RNPM_VFMAILBOX_SIZE); + return -EINVAL; + } + + if (rnpm_logd_level(LOG_MBX_OUT)) { + rnpm_logd(LOG_MBX_OUT, "%x mbx_out:", hw->pfvfnum); + for (i = 0; i < 4; i++) + rnpm_logd(LOG_MBX_OUT, " 0x%08x ", msg[i]); + rnpm_logd(LOG_MBX_OUT, "\n"); + } + +retry: + wait_msg_free_cnt--; + if (wait_msg_free_cnt > 0 && mbx_rd32(hw, DATA_REG) != 0) { + udelay(1000); + // unlock + goto retry; + } + + /* lock the mailbox to prevent pf/vf/cpu race condition */ + ret_val = rnpm_obtain_mbx_lock_pf(hw, mbx_id); + if (ret_val) { + rnpm_err( + "%s: get mbx:%d wlock failed. ret:%d. req:0x%08x-0x%08x\n", + __func__, mbx_id, ret_val, msg[0], msg[1]); + goto out_no_write; + } + + /* copy the caller specified message to the mailbox memory buffer */ + for (i = 0; i < size; i++) { +#ifdef MBX_WR_DEBUG + mbx_pwr32(hw, DATA_REG + i * 4, msg[i]); +#else + mbx_wr32(hw, DATA_REG + i * 4, msg[i]); +#endif + } + + /* flush msg and acks as we are overwriting the message buffer */ + if (mbx_id == MBX_CM3CPU) { + hw->mbx.cpu_ack = rnpm_mbx_get_ack(hw, CPU2PF_COUNTER); + } else { + hw->mbx.vf_ack[mbx_id] = + rnpm_mbx_get_ack(hw, VF2PF_COUNTER(mbx_id)); + } + rnpm_mbx_inc_pf_req(hw, mbx_id); + /* Interrupt VF/CM3 to tell it a message has + * been sent and release buffer + */ + udelay(30); + mbx_wr32(hw, CTRL_REG, MBOX_CTRL_REQ); +out_no_write: + /* sometimes happen */ + /*printk("cannot get lock\n"); */ + + return ret_val; +} + +/** + * rnpm_read_mbx_pf - Read a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @vf_number: the VF index + * + * This function copies a message from the mailbox buffer to the caller's + * memory buffer. The presumption is that the caller knows that there was + * a message due to a VF/CPU request so no polling for message is needed. + **/ +static s32 rnpm_read_mbx_pf(struct rnpm_hw *hw, u32 *msg, u16 size, + enum MBX_ID mbx_id) +{ + s32 ret_val = -EIO; + u32 i; + u32 BUF_REG = (mbx_id == MBX_CM3CPU) ? CPU_PF_SHM_DATA : + PF_VF_SHM_DATA(mbx_id); + u32 CTRL_REG = (mbx_id == MBX_CM3CPU) ? PF2CPU_MBOX_CTRL : + PF2VF_MBOX_CTRL(mbx_id); + + if (size > RNPM_VFMAILBOX_SIZE) { + rnpm_info("%s: size:%d should <%d\n", __func__, size, + RNPM_VFMAILBOX_SIZE); + return -EINVAL; + } + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = rnpm_obtain_mbx_lock_pf(hw, mbx_id); + if (ret_val) { + ret_val = -EPERM; + goto out_no_read; + } + /* memory barrior */ + mb(); + /* copy the message from the mailbox memory buffer */ + for (i = 0; i < size; i++) { +#ifdef MBX_RD_DEBUG + msg[i] = mbx_prd32(hw, BUF_REG + 4 * i); +#else + msg[i] = mbx_rd32(hw, BUF_REG + 4 * i); +#endif + } + // zero opcode + mbx_wr32(hw, BUF_REG, 0); + + /* update req. used by rnpmvf_check_for_msg_vf */ + if (mbx_id == MBX_CM3CPU) { + hw->mbx.cpu_req = rnpm_mbx_get_req(hw, CPU2PF_COUNTER); + } else { + hw->mbx.vf_req[mbx_id] = + rnpm_mbx_get_req(hw, VF2PF_COUNTER(mbx_id)); + } + /* this ack maybe too earier? */ + /* Acknowledge receipt and release mailbox, then we're done */ + rnpm_mbx_inc_pf_ack(hw, mbx_id); + + /* free ownership of the buffer */ + mbx_wr32(hw, CTRL_REG, 0); + + if (rnpm_logd_level(LOG_MBX_IN)) { + rnpm_logd(LOG_MBX_IN, "%x mbx_in :", hw->pfvfnum); + for (i = 0; i < 16; i++) + rnpm_logd(LOG_MBX_IN, "0x%08x ", msg[i]); + rnpm_logd(LOG_MBX_IN, "\n"); + } + +out_no_read: + + return ret_val; +} + +static void rnpm_mbx_reset(struct rnpm_hw *hw) +{ + int idx, v; + + for (idx = 0; idx < RNP_MAX_VF_FUNCTIONS; idx++) { + v = mbx_rd32(hw, VF2PF_COUNTER(idx)); + hw->mbx.vf_req[idx] = v & 0xffff; + hw->mbx.vf_ack[idx] = (v >> 16) & 0xffff; + + // release pf<->vf pfu buffer lock + mbx_wr32(hw, PF2VF_MBOX_CTRL(idx), 0); + } + // reset pf->cm3 status + v = mbx_rd32(hw, CPU2PF_COUNTER); + hw->mbx.cpu_req = v & 0xffff; + hw->mbx.cpu_ack = (v >> 16) & 0xffff; + // release pf->cm3 buffer lock + mbx_wr32(hw, PF2CPU_MBOX_CTRL, 0); + + wr32(hw, PF_VF_MBOX_MASK_LO, 0); // allow vf to vectors + wr32(hw, PF_VF_MBOX_MASK_HI, 0); // enable irq + + // allow CM3CPU to PF MBX IRQ + wr32(hw, CPU_PF_MBOX_MASK, 0); +} + +static int rnpm_mbx_configure_pf(struct rnpm_hw *hw, int nr_vec, bool enable) +{ + int idx = 0; + u32 v; + + // dump_stack(); + + if (enable) { + // hw->mbx.irq_enabled = true; + for (idx = 0; idx < RNPM_MAX_VF_FUNCTIONS; idx++) { + v = mbx_rd32(hw, VF2PF_COUNTER(idx)); + hw->mbx.vf_req[idx] = v & 0xffff; + hw->mbx.vf_ack[idx] = (v >> 16) & 0xffff; + + /* release pf<->vf pfu buffer lock */ + mbx_wr32(hw, PF2VF_MBOX_CTRL(idx), 0); + } + /* reset pf->cm3 status */ + v = mbx_rd32(hw, CPU2PF_COUNTER); + hw->mbx.cpu_req = v & 0xffff; + hw->mbx.cpu_ack = (v >> 16) & 0xffff; + /* release pf->cm3 buffer lock */ + mbx_wr32(hw, PF2CPU_MBOX_CTRL, 0); + + /* allow VF to PF MBX IRQ */ + for (idx = 0; idx < RNPM_MAX_VF_FUNCTIONS; idx++) { + /* vf to pf req interrupt */ + mbx_wr32(hw, VF2PF_MBOX_VEC(idx), nr_vec); + } + + wr32(hw, PF_VF_MBOX_MASK_LO, 0); // allow vf to vectors + wr32(hw, PF_VF_MBOX_MASK_HI, 0); // enable irq + + // bind cm3cpu mbx to irq + // cm3 and VF63 share #63 irq + wr32(hw, CPU2PF_MBOX_VEC, nr_vec); + // allow CM3CPU to PF MBX IRQ + wr32(hw, CPU_PF_MBOX_MASK, 0); + } else { + // hw->mbx.irq_enabled = false; + + wr32(hw, PF_VF_MBOX_MASK_LO, 0xffffffff); // disable irq + wr32(hw, PF_VF_MBOX_MASK_HI, 0xffffffff); // disable irq + + // disable CM3CPU to PF MBX IRQ + wr32(hw, CPU_PF_MBOX_MASK, 0xffffffff); + + // reset vf->pf status/ctrl + for (idx = 0; idx < RNPM_MAX_VF_FUNCTIONS; idx++) + mbx_wr32(hw, PF2VF_MBOX_CTRL(idx), 0); + + // reset pf->cm3 ctrl + mbx_wr32(hw, PF2CPU_MBOX_CTRL, 0); + + wr32(hw, RNPM_DMA_DUMY, 0); + } + + return 0; +} + +/** + * rnpm_init_mbx_params_pf - set initial values for pf mailbox + * @hw: pointer to the HW structure + * + * Initializes the hw->mbx struct to correct values for pf mailbox + */ +s32 rnpm_init_mbx_params_pf(struct rnpm_hw *hw) +{ + struct rnpm_mbx_info *mbx = &hw->mbx; + + mbx->usec_delay = 100; + // wait 5s + mbx->timeout = (4 * 1000 * 1000) / mbx->usec_delay; + + mbx->stats.msgs_tx = 0; + mbx->stats.msgs_rx = 0; + mbx->stats.reqs = 0; + mbx->stats.acks = 0; + mbx->stats.rsts = 0; + + mbx->size = RNPM_VFMAILBOX_SIZE; + + mbx->reply_dma_size = 4096; + mbx->reply_dma = dma_alloc_coherent(&hw->pdev->dev, mbx->reply_dma_size, + &mbx->reply_dma_phy, GFP_ATOMIC); + if (!mbx->reply_dma) { + mbx->reply_dma = + dma_alloc_coherent(&hw->pdev->dev, mbx->reply_dma_size, + &mbx->reply_dma_phy, GFP_ATOMIC); + if (!mbx->reply_dma) { + rnpm_err("%s: dma_alloc_coherent failed! %p\n", __func__, + mbx->reply_dma); + mbx->reply_dma = NULL; + mbx->reply_dma_size = 0; + } + } + + rnpm_mbx_reset(hw); + + return 0; +} + +struct rnpm_mbx_operations mbx_ops_generic = { + .init_params = rnpm_init_mbx_params_pf, + .read = rnpm_read_mbx_pf, + .write = rnpm_write_mbx_pf, + .read_posted = rnpm_read_posted_mbx, + .write_posted = rnpm_write_posted_mbx, + .check_for_msg = rnpm_check_for_msg_pf, + .check_for_ack = rnpm_check_for_ack_pf, + .configure = rnpm_mbx_configure_pf, +}; diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.h b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.h new file mode 100644 index 000000000000..6611b4addd89 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_MBX_H_ +#define _RNPM_MBX_H_ + +#include "rnpm_type.h" + +#define RNPM_VFMAILBOX_SIZE 14 /* 16 32 bit words - 64 bytes */ +#define RNPM_ERR_MBX -100 + +#define RNPM_VT_MSGTYPE_ACK 0x80000000 +/* Messages below or'd with + * this are the ACK + */ +#define RNPM_VT_MSGTYPE_NACK 0x40000000 +/* Messages below or'd with + * this are the NACK + */ +#define RNPM_VT_MSGTYPE_CTS 0x20000000 +/* Indicates that VF is still + * clear to send requests + */ +#define RNPM_VT_MSGINFO_SHIFT 16 +/* bits 23:16 are used for exra info for certain messages */ +#define RNPM_VT_MSGINFO_MASK (0xFF << RNPM_VT_MSGINFO_SHIFT) +/* VLAN pool filtering masks */ +#define RNPM_VLVF_VIEN 0x80000000 /* filter is valid */ +#define RNPM_VLVF_ENTRIES 64 +#define RNPM_VLVF_VLANID_MASK 0x00000FFF + +/* mailbox API, legacy requests */ +#define RNPM_VF_RESET 0x01 /* VF requests reset */ +#define RNPM_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */ +#define RNPM_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */ +#define RNPM_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */ + +/* mailbox API, version 1.0 VF requests */ +#define RNPM_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */ +#define RNPM_VF_SET_MACVLAN 0x06 /* VF requests PF for unicast filter */ +#define RNPM_VF_API_NEGOTIATE 0x08 /* negotiate API version */ + +/* mailbox API, version 1.1 VF requests */ +#define RNPM_VF_GET_QUEUES 0x09 /* get queue configuration */ +#define RNPM_VF_SET_VLAN_STRIP 0x0a /* VF requests PF to set VLAN STRIP */ +#define RNPM_VF_REG_RD 0x0b /* vf read reg */ +#define RNPM_VF_GET_STATUS 0x0c /* vf get pf ethtool setup */ + +#define RNPM_PF_SET_FCS 0x0c /* PF set fcs status */ +#define RNPM_PF_SET_PAUSE 0x0d /* PF set pause status */ +#define RNPM_PF_SET_FT_PADDING 0x0e /* PF set ft padding status */ + +#define RNPM_PF_REMOVE 0x0f +/* GET_QUEUES return data indices within the mailbox */ +#define RNPM_VF_TX_QUEUES 1 /* number of Tx queues supported */ +#define RNPM_VF_RX_QUEUES 2 /* number of Rx queues supported */ +#define RNPM_VF_TRANS_VLAN 3 /* Indication of port vlan */ +#define RNPM_VF_DEF_QUEUE 4 /* Default queue offset */ + +#define RNPM_VF_GET_LINK 0x10 /* get link status */ + +/* length of permanent address message returned from PF */ +#define RNPM_VF_PERMADDR_MSG_LEN 5 +/* word in permanent address message with the current multicast type */ +#define RNPM_VF_MC_TYPE_WORD 3 +#define RNPM_VF_DMA_VERSION_WORD 4 + +#define RNPM_PF_CONTROL_PRING_MSG 0x0100 /* PF control message */ + +#define RNPM_VF_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */ +#define RNPM_VF_MBX_INIT_DELAY 500 /* microseconds between retries */ + +enum MBX_ID { + MBX_VF0 = 0, + MBX_VF1, + //... + MBX_VF63, + MBX_CM3CPU, + MBX_FW = MBX_CM3CPU, + MBX_VFCNT +}; + +enum PF_STATUS { + PF_FCS_STATUS, + PF_PAUSE_STATUS, + PF_FT_PADDING_STATUS, + PF_VLAN_FILTER_STATUS, +}; + +s32 rnpm_read_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, enum MBX_ID); +s32 rnpm_write_mbx(struct rnpm_hw *hw, u32 *msg, u16 size, enum MBX_ID); +s32 rnpm_check_for_msg(struct rnpm_hw *hw, enum MBX_ID); +s32 rnpm_check_for_ack(struct rnpm_hw *hw, enum MBX_ID); +s32 rnpm_check_for_rst(struct rnpm_hw *hw, enum MBX_ID); + +s32 rnpm_init_mbx_params_pf(struct rnpm_hw *hw); + +extern struct rnpm_mbx_operations mbx_ops_generic; +struct rnpm_info; +struct rnpm_pf_adapter; + +int rnpm_fw_get_macaddr(struct rnpm_hw *hw, int pfvfnum, u8 *mac_addr, + int lane); +int rnpm_mbx_fw_reset_phy(struct rnpm_hw *hw); +int rnpm_mbx_get_capability(struct rnpm_hw *hw, struct rnpm_info *info); +int rnpm_fw_msg_handler(struct rnpm_pf_adapter *pf_adapter); +int rnpm_fw_update(struct rnpm_hw *hw, int partition, const u8 *fw_bin, + int bytes); +int rnpm_mbx_pf_link_event_enable(struct rnpm_hw *hw, int enable); +int rnpm_mbx_pf_link_event_enable_nolock(struct rnpm_hw *hw, int enable); +int rnpm_mbx_lane_link_changed_event_enable(struct rnpm_hw *hw, int enable); + +int rnpm_mbx_led_set(struct rnpm_hw *hw, int value); +#define MBX_IFDOWN (0) +#define MBX_IFUP (1) +#define MBX_PROBE (2) +#define MBX_REMOVE (3) +int rnpm_mbx_ifup_down(struct rnpm_hw *hw, int up); + +void rnpm_link_stat_mark_reset(struct rnpm_hw *hw); + +int rnpm_mbx_sfp_module_eeprom_info(struct rnpm_hw *hw, int sfp_addr, int reg, + int data_len, u8 *buf); +// int rnpm_mbx_sfp_read(struct rnpm_hw *hw, int sfp_addr, int reg); +int rnpm_mbx_sfp_write(struct rnpm_hw *hw, int sfp_addr, int reg, short v); +int rnpm_mbx_get_dump(struct rnpm_hw *hw, int flags, u8 *data_out, int bytes); +int rnpm_mbx_set_dump(struct rnpm_hw *hw, int flag); +int rnpm_mbx_phy_link_set(struct rnpm_hw *hw, int speeds); +int rnpm_mbx_get_temp(struct rnpm_hw *hw, int *voltage); +int rnpm_maintain_req(struct rnpm_hw *hw, int cmd, int arg0, int req_data_bytes, + int reply_bytes, dma_addr_t dma_phy_addr); +int rnpm_mbx_get_lane_stat(struct rnpm_hw *hw); +int rnpm_set_lane_fun(struct rnpm_hw *hw, int fun, int value0, int value1, + int value2, int value3); +void rnpm_link_stat_mark(struct rnpm_hw *hw, int nr_lane, int up); +void rnpm_mbx_probe_stat_set(struct rnpm_pf_adapter *pf_adapter, int probe); +int rnpm_mbx_get_phy_statistics(struct rnpm_hw *hw, u8 *data); +int rnpm_mbx_get_link(struct rnpm_hw *hw); +int rnpm_hw_set_clause73_autoneg_enable(struct rnpm_hw *hw, int enable); +int rnpm_hw_set_fw_10g_1g_auto_detch(struct rnpm_hw *hw, int enable); +int rnpm_mbx_reg_writev(struct rnpm_hw *hw, int fw_reg, int value[4], + int bytes); +int rnpm_mbx_reg_write(struct rnpm_hw *hw, int fw_reg, int value); +int rnpm_mbx_fw_reg_read(struct rnpm_hw *hw, int fw_reg); +int rnpm_mbx_wol_set(struct rnpm_hw *hw, u32 mode); +int rnpm_mbx_force_speed(struct rnpm_hw *hw, int speed); +int rnpm_mbx_lldp_status_get(struct rnpm_hw *hw); +int rnpm_mbx_lldp_port_enable(struct rnpm_hw *hw, bool enable); + +#define cm3_reg_write32(hw, cm3_rpu_reg, v) \ + rnpm_mbx_reg_write((hw), (cm3_rpu_reg), (v)) + +#define cm3_reg_read32(hw, cm3_rpu_reg) \ + rnpm_mbx_fw_reg_read((hw), (cm3_rpu_reg)) + +#endif /* _RNPM_MBX_H_ */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.c b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.c new file mode 100644 index 000000000000..871b72ed13d9 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.c @@ -0,0 +1,1521 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include + +#include "rnpm.h" +#include "rnpm_mbx.h" +#include "rnpm_mbx_fw.h" + +#define RNP_FW_MAILBOX_SIZE RNPM_VFMAILBOX_SIZE + +struct mbx_req_cookie *mbx_cookie_zalloc(int priv_len) +{ + struct mbx_req_cookie *cookie = + kzalloc(sizeof(*cookie) + priv_len, GFP_KERNEL); + + if (cookie) { + cookie->timeout_jiffes = 30 * HZ; + cookie->magic = COOKIE_MAGIC; + cookie->priv_len = priv_len; + } + + return cookie; +} + +int rnpm_mbx_write_posted_locked(struct rnpm_hw *hw, struct mbx_fw_cmd_req *req) +{ + int err = 0; + int retry = 3; + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(hw->pdev); + + if (mutex_lock_interruptible(hw->mbx.lock)) { + rnpm_err("[%s] get mbx lock failed opcode:0x%x\n", __func__, + req->opcode); + return -EAGAIN; + } + if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) { + err = RNPM_MBX_ERR_IN_REMOVING; + goto error; + } + rnpm_logd(LOG_MBX_LOCK, "%s %d lock:%p hw:%p opcode:0x%x\n", __func__, + hw->pfvfnum, hw->mbx.lock, hw, req->opcode); + +try_again: + retry--; + if (retry < 0) { + mutex_unlock(hw->mbx.lock); + rnpm_err("%s: write_posted failed! err:0x%x opcode:0x%x\n", + __func__, err, req->opcode); + return -EIO; + } + + err = hw->mbx.ops.write_posted( + hw, (u32 *)req, (req->datalen + MBX_REQ_HDR_LEN) / 4, MBX_FW); + if (err) + goto try_again; + +error: + mutex_unlock(hw->mbx.lock); + + return err; +} + +/* force firmware report link event to driver */ +void rnpm_link_stat_mark_reset(struct rnpm_hw *hw) +{ + wr32(hw, RNPM_DMA_DUMY, 0xa5a40000); +} + +void rnpm_link_stat_mark_disable(struct rnpm_hw *hw) +{ + wr32(hw, RNPM_DMA_DUMY, 0x0); +} + +int rnpm_mbx_fw_post_req(struct rnpm_hw *hw, struct mbx_fw_cmd_req *req, + struct mbx_req_cookie *cookie) +{ + int err = 0; + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(hw->pdev); + + cookie->errcode = 0; + cookie->done = 0; + init_waitqueue_head(&cookie->wait); + + if (mutex_lock_interruptible(hw->mbx.lock)) { + rnpm_err("[%s] wait mbx lock timeout opcode:0x%x\n", __func__, + req->opcode); + return -EAGAIN; + } + + if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) { + err = RNPM_MBX_ERR_IN_REMOVING; + goto error; + } + + rnpm_logd(LOG_MBX_LOCK, "%s %d lock:%p hw:%p opcode:0x%x\n", __func__, + hw->pfvfnum, hw->mbx.lock, hw, req->opcode); + + err = rnpm_write_mbx(hw, (u32 *)req, + (req->datalen + MBX_REQ_HDR_LEN) / 4, MBX_FW); + if (err) { + rnpm_err("rnpm_write_mbx failed! err:%d opcode:0x%x\n", err, + req->opcode); + mutex_unlock(hw->mbx.lock); + return err; + } + + if (cookie->timeout_jiffes != 0) { +retry: + err = wait_event_interruptible_timeout(cookie->wait, + cookie->done == 1, + cookie->timeout_jiffes); + if (err == -ERESTARTSYS) + goto retry; + if (err == 0) { + rnpm_err("%s failed! timeout err:%d opcode:%x\n", + __func__, err, req->opcode); + err = -ETIME; + } else { + err = 0; + } + } else { + wait_event_interruptible(cookie->wait, cookie->done == 1); + } + + if (cookie->errcode) + err = cookie->errcode; + +error: + mutex_unlock(hw->mbx.lock); + + return err; +} + +int rnpm_fw_send_cmd_wait(struct rnpm_hw *hw, struct mbx_fw_cmd_req *req, + struct mbx_fw_cmd_reply *reply) +{ + int err = 0; + int retry_cnt = 3; + struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(hw->pdev); + + if (!hw || !req || !reply || !hw->mbx.ops.read_posted) { + rnpm_err("error: hw:%p req:%p reply:%p\n", hw, req, reply); + return -EINVAL; + } + + if (mutex_lock_interruptible(hw->mbx.lock)) { + rnpm_err("[%s] get mbx lock failed opcode:0x%x\n", __func__, + req->opcode); + return -EAGAIN; + } + + if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) { + err = RNPM_MBX_ERR_IN_REMOVING; + goto error; + } + + rnpm_logd(LOG_MBX_LOCK, "%s %d lock:%p hw:%p opcode:0x%x\n", __func__, + hw->pfvfnum, hw->mbx.lock, hw, req->opcode); + + err = hw->mbx.ops.write_posted( + hw, (u32 *)req, (req->datalen + MBX_REQ_HDR_LEN) / 4, MBX_FW); + if (err) { + rnpm_err("%s: write_posted failed! err:0x%x opcode:0x%x\n", + __func__, err, req->opcode); + goto quit; + } + +// ignore link-status event +retry: + retry_cnt--; + if (retry_cnt < 0) { + err = -EIO; + goto quit; + } + err = hw->mbx.ops.read_posted(hw, (u32 *)reply, sizeof(*reply) / 4, + MBX_FW); + if (err) { + rnpm_err("%s: read_posted failed! err:0x%x opcode:0x%x\n", + __func__, err, req->opcode); + mutex_unlock(hw->mbx.lock); + return err; + } + if (reply->opcode != req->opcode) + goto retry; + + if (req->reply_lo) { + memcpy(reply, hw->mbx.reply_dma, sizeof(*reply)); + memset(hw->mbx.reply_dma, 0, 16); + } + + if (reply->error_code) { + rnpm_err("%s: reply err:0x%x req:0x%x\n", __func__, + reply->error_code, req->opcode); + err = -reply->error_code; + goto quit; + } +quit: +error: + mutex_unlock(hw->mbx.lock); + return err; +} + +int rnpm_mbx_get_link(struct rnpm_hw *hw) +{ + struct rnpm_adapter *adpt = hw->back; + int v = rd32(hw, RNPM_TOP_NIC_DUMMY); + + if ((v & 0xff000000) == 0xa5000000) { + hw->link = (v & BIT(hw->nr_lane)) ? 1 : 0; + adpt->flags |= RNPM_FLAG_NEED_LINK_UPDATE; + + return 0; + } + return -1; +} + +int rnpm_mbx_get_lane_stat(struct rnpm_hw *hw) +{ + int err = 0; + struct mbx_fw_cmd_req req; + struct rnpm_adapter *adpt = hw->back; + struct lane_stat_data *st; + struct mbx_req_cookie *cookie = NULL; + struct mbx_fw_cmd_reply reply; + + memset(&req, 0, sizeof(req)); + + if (hw->mbx.irq_enabled) { + cookie = mbx_cookie_zalloc(sizeof(struct lane_stat_data)); + + if (!cookie) { + rnpm_err("%s: no memory\n", __func__); + return -ENOMEM; + } + + st = (struct lane_stat_data *)cookie->priv; + + build_get_lane_status_req(&req, hw->nr_lane, cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + if (err != RNPM_MBX_ERR_IN_REMOVING) { + rnpm_err("%s: error:%d\n", __func__, err); + WARN_ON(1); + } + goto quit; + } + } else { + memset(&reply, 0, sizeof(reply)); + + build_get_lane_status_req(&req, hw->nr_lane, &req); + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + + if (err) { + if (err != RNPM_MBX_ERR_IN_REMOVING) + rnpm_err("%s: 1 error:%d\n", __func__, err); + goto quit; + } + st = (struct lane_stat_data *)&(reply.data); + } + + hw->phy_type = st->phy_type; + hw->speed = adpt->speed = st->speed; + if (st->is_sgmii) { + adpt->phy_addr = st->phy_addr; + } else { + adpt->sfp.fault = st->sfp.fault; + adpt->sfp.los = st->sfp.los; + adpt->sfp.mod_abs = st->sfp.mod_abs; + adpt->sfp.tx_dis = st->sfp.tx_dis; + } + + adpt->si.main = st->si_main; + adpt->si.pre = st->si_pre; + adpt->si.post = st->si_post; + adpt->si.tx_boost = st->si_tx_boost & 0xf; + adpt->an = st->an; + adpt->link_traing = st->link_traing; + adpt->fec = st->fec; + hw->is_sgmii = st->is_sgmii; + hw->pci_gen = st->pci_gen; + hw->pci_lanes = st->pci_lanes; + adpt->speed = st->speed; + adpt->hw.link = st->linkup; + hw->is_backplane = st->is_backplane; + hw->supported_link = st->supported_link; + if (hw->fw_version <= 0x00050000) + hw->duplex = 1; + else + hw->duplex = st->duplex; + + rnpm_logd( + LOG_MBX_LINK_STAT, + "%s:pma_type:0x%x phy_type:0x%x,linkup:%d speed=%d duplex:%d auton:%d ", + adpt->name, st->pma_type, st->phy_type, st->linkup, st->speed, + st->duplex, st->autoneg); + rnpm_logd( + LOG_MBX_LINK_STAT, + "fec:%d an:%d lt:%d is_sgmii:%d supported_link:0x%x, backplane:%d ", + st->fec, st->an, st->link_traing, st->is_sgmii, + hw->supported_link, hw->is_backplane); + rnpm_logd(LOG_MBX_LINK_STAT, "phy_addr:0x%x\n", adpt->phy_addr); +quit: + kfree(cookie); + return err; +} + +int rnpm_mbx_get_phy_statistics(struct rnpm_hw *hw, u8 *data) +{ + int err = 0; + struct mbx_fw_cmd_req req; + + memset(&req, 0, sizeof(req)); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = + mbx_cookie_zalloc(sizeof(struct phy_statistics)); + + if (!cookie) + return -ENOMEM; + + build_get_phy_statistics_req(&req, hw->nr_lane, cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err == 0) { + memcpy(data, cookie->priv, + sizeof(struct phy_statistics)); + } + + kfree(cookie); + } else { + struct mbx_fw_cmd_reply reply; + + memset(&reply, 0, sizeof(reply)); + + build_get_phy_statistics_req(&req, hw->nr_lane, &req); + return rnpm_fw_send_cmd_wait(hw, &req, &reply); + } + + return err; +} + +int rnpm_mbx_fw_reset_phy(struct rnpm_hw *hw) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + int ret; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = mbx_cookie_zalloc(0); + + if (!cookie) + return -ENOMEM; + + build_reset_phy_req(&req, cookie); + + ret = rnpm_mbx_fw_post_req(hw, &req, cookie); + kfree(cookie); + } else { + build_reset_phy_req(&req, &req); + ret = rnpm_fw_send_cmd_wait(hw, &req, &reply); + } + return ret; +} + +int rnpm_maintain_req(struct rnpm_hw *hw, int cmd, int arg0, int req_data_bytes, + int reply_bytes, dma_addr_t dma_phy_addr) +{ + int err; + struct mbx_req_cookie *cookie = NULL; + u64 address = dma_phy_addr; + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + cookie = mbx_cookie_zalloc(0); + if (!cookie) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + cookie->timeout_jiffes = 60 * HZ; // 60s + + build_maintain_req(&req, cookie, cmd, arg0, req_data_bytes, reply_bytes, + address & 0xffffffff, (address >> 32) & 0xffffffff); + + if (hw->mbx.irq_enabled) { + cookie->timeout_jiffes = 400 * HZ; + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + } else { + int old_mbx_timeout = hw->mbx.timeout; + + hw->mbx.timeout = + (400 * 1000 * 1000) / hw->mbx.usec_delay; // wait 30s + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + hw->mbx.timeout = old_mbx_timeout; + } + + kfree(cookie); + return (err) ? -EIO : 0; +} + +int rnpm_fw_get_macaddr(struct rnpm_hw *hw, int pfvfnum, u8 *mac_addr, + int nr_lane) +{ + int err; + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + rnpm_dbg("%s: pfvfnum:0x%x nr_lane:%d\n", __func__, pfvfnum, nr_lane); + + if (!mac_addr) { + rnpm_err("%s: mac_addr is null\n", __func__); + return -EINVAL; + } + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = + mbx_cookie_zalloc(sizeof(reply.mac_addr)); + struct mac_addr *mac; + + if (!cookie) + return -ENOMEM; + mac = (struct mac_addr *)cookie->priv; + + build_get_macaddress_req(&req, 1 << nr_lane, pfvfnum, cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + kfree(cookie); + return err; + } + hw->ccode = mac->ccode; + + if ((1 << nr_lane) & mac->lanes) { + memcpy(mac_addr, mac->addrs[nr_lane].mac, 6); + kfree(cookie); + return 0; + } + kfree(cookie); + return -ENODATA; + } + build_get_macaddress_req(&req, 1 << nr_lane, pfvfnum, &req); + + // mbx_fw_req_set_reply(&req, hw->mbx.reply_dma_phy); + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + + if (err) { + if (err != RNPM_MBX_ERR_IN_REMOVING) + rnpm_err("%s: failed. err:%d\n", __func__, err); + return err; + } + + hw->ccode = reply.mac_addr.ccode; + if ((1 << nr_lane) & reply.mac_addr.lanes) { + memcpy(mac_addr, reply.mac_addr.addrs[nr_lane].mac, 6); + return 0; + } + + return -ENODATA; +} + +static int rnpm_mbx_sfp_read(struct rnpm_hw *hw, int sfp_i2c_addr, int reg, + int cnt, u8 *out_buf) +{ + struct mbx_fw_cmd_req req; + int err = -EIO; + int nr_lane = hw->nr_lane; + + if ((cnt > MBX_SFP_READ_MAX_CNT) || !out_buf) { + rnpm_err("%s: cnt:%d should <= %d out_buf:%p\n", __func__, cnt, + MBX_SFP_READ_MAX_CNT, out_buf); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = mbx_cookie_zalloc(cnt); + + if (!cookie) + return -ENOMEM; + build_mbx_sfp_read(&req, nr_lane, sfp_i2c_addr, reg, cnt, + cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + kfree(cookie); + return err; + } + memcpy(out_buf, cookie->priv, cnt); + err = 0; + kfree(cookie); + } else { + struct mbx_fw_cmd_reply reply; + + memset(&reply, 0, sizeof(reply)); + build_mbx_sfp_read(&req, nr_lane, sfp_i2c_addr, reg, cnt, + &reply); + + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + if (err == 0) + memcpy(out_buf, reply.sfp_read.value, cnt); + } + + return err; +} + +int rnpm_mbx_sfp_module_eeprom_info(struct rnpm_hw *hw, int sfp_addr, int reg, + int data_len, u8 *buf) +{ + int left = data_len; + int cnt, err; + + do { + cnt = (left > MBX_SFP_READ_MAX_CNT) ? MBX_SFP_READ_MAX_CNT : + left; + err = rnpm_mbx_sfp_read(hw, sfp_addr, reg, cnt, buf); + if (err) { + rnpm_err("%s: error:%d\n", __func__, err); + return err; + } + reg += cnt; + buf += cnt; + left -= cnt; + } while (left > 0); + + return 0; +} + +int rnpm_mbx_sfp_write(struct rnpm_hw *hw, int sfp_addr, int reg, short v) +{ + struct mbx_fw_cmd_req req; + int err; + int nr_lane = hw->nr_lane; + + memset(&req, 0, sizeof(req)); + + build_mbx_sfp_write(&req, nr_lane, sfp_addr, reg, v); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_fw_reg_read(struct rnpm_hw *hw, int fw_reg) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + int err, ret = 0xffffffff; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = + mbx_cookie_zalloc(sizeof(reply.r_reg)); + + if (!cookie) + return -ENOMEM; + build_readreg_req(&req, fw_reg, cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + kfree(cookie); + return ret; + } + ret = ((int *)(cookie->priv))[0]; + } else { + build_readreg_req(&req, fw_reg, &reply); + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + if (err) { + if (err != RNPM_MBX_ERR_IN_REMOVING) + rnpm_err("%s: failed. err:%d\n", __func__, err); + return err; + } + ret = reply.r_reg.value[0]; + } + return ret; +} + +int rnpm_mbx_reg_write(struct rnpm_hw *hw, int fw_reg, int value) +{ + struct mbx_fw_cmd_req req; + int err; + + memset(&req, 0, sizeof(req)); + + if (hw->fw_version < 0x00050200) + return -EOPNOTSUPP; + + build_writereg_req(&req, NULL, fw_reg, 4, &value); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_reg_writev(struct rnpm_hw *hw, int fw_reg, int value[4], int bytes) +{ + struct mbx_fw_cmd_req req; + int err; + + memset(&req, 0, sizeof(req)); + + build_writereg_req(&req, NULL, fw_reg, bytes, value); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_lldp_all_ports_enable(struct rnpm_hw *hw, bool enable) +{ + struct mbx_fw_cmd_req req; + int err; + + if (!hw->fw_lldp_ablity) + return -EOPNOTSUPP; + + memset(&req, 0, sizeof(req)); + + build_lldp_ctrl_set(&req, LLDP_TX_ALL_LANES, enable); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_lldp_port_enable(struct rnpm_hw *hw, bool enable) +{ + struct mbx_fw_cmd_req req; + int err; + int nr_lane = hw->nr_lane; + + if (!hw->fw_lldp_ablity) { + rnpm_warn("lldp set not supported\n"); + return -EOPNOTSUPP; + } + + memset(&req, 0, sizeof(req)); + + build_lldp_ctrl_set(&req, nr_lane, enable); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_lldp_status_get(struct rnpm_hw *hw) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + int err, ret = 0; + + if (!hw->fw_lldp_ablity) { + rnpm_warn("fw lldp not supported\n"); + return -EOPNOTSUPP; + } + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = + mbx_cookie_zalloc(sizeof(reply.lldp)); + + if (!cookie) + return -ENOMEM; + build_lldp_ctrl_get(&req, hw->nr_lane, cookie); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + kfree(cookie); + return ret; + } + ret = ((int *)(cookie->priv))[0]; + } else { + build_lldp_ctrl_get(&req, hw->nr_lane, &reply); + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + if (err) { + if (err != RNPM_MBX_ERR_IN_REMOVING) + rnpm_err("%s: failed. err:%d\n", __func__, err); + return err; + } + ret = reply.lldp.enable_stat; + } + return ret; +} + +int rnpm_mbx_wol_set(struct rnpm_hw *hw, u32 mode) +{ + struct mbx_fw_cmd_req req; + int err; + int nr_lane = hw->nr_lane; + + memset(&req, 0, sizeof(req)); + + build_mbx_wol_set(&req, nr_lane, mode); + + err = rnpm_mbx_write_posted_locked(hw, &req); + return err; +} + +int rnpm_mbx_set_dump(struct rnpm_hw *hw, int flag) +{ + int err; + struct mbx_fw_cmd_req req; + + memset(&req, 0, sizeof(req)); + build_set_dump(&req, hw->nr_lane, flag); + + err = rnpm_mbx_write_posted_locked(hw, &req); + + return err; +} + +int rnpm_mbx_get_dump(struct rnpm_hw *hw, int flags, u8 *data_out, int bytes) +{ + int err; + struct mbx_req_cookie *cookie = NULL; + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + struct get_dump_reply *get_dump; + u64 address; + void *dma_buf = NULL; + dma_addr_t dma_phy = 0; + + cookie = mbx_cookie_zalloc(sizeof(*get_dump)); + if (!cookie) + return -ENOMEM; + get_dump = (struct get_dump_reply *)cookie->priv; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + if (bytes > sizeof(get_dump->data)) { + dma_buf = dma_alloc_coherent(&hw->pdev->dev, bytes, &dma_phy, + GFP_ATOMIC); + if (!dma_buf) { + err = -ENOMEM; + goto quit; + } + } + + address = dma_phy; + build_get_dump_req(&req, cookie, hw->nr_lane, address & 0xffffffff, + (address >> 32) & 0xffffffff, bytes); + + if (hw->mbx.irq_enabled) { + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + } else { + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + get_dump = &reply.get_dump; + } + +quit: + if (err == 0) { + hw->dump.version = get_dump->version; + hw->dump.flag = get_dump->flags; + hw->dump.len = get_dump->bytes; + } + if (err == 0 && data_out) { + if (dma_buf) + memcpy(data_out, dma_buf, bytes); + else + memcpy(data_out, get_dump->data, bytes); + } + if (dma_buf) { + // pci_free_consistent(hw->pdev, bytes, dma_buf, dma_phy); + dma_free_coherent(&hw->pdev->dev, bytes, dma_buf, dma_phy); + } + kfree(cookie); + return err ? -err : 0; +} + +/** + *@speed : + *0 : disable force speed + *1000 : force 1000Mbps + *10000 : force 10000Mbps + */ +int rnpm_mbx_force_speed(struct rnpm_hw *hw, int speed) +{ + int cmd = 0x01150000; // disable force speed + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + + if ((hw->fw_version < 0x00050201) || hw->is_sgmii) + return -EINVAL; + + /* adapter_cnt: 4 ---- cmd BIT 9 is 1 + * adapter_cnt: 1/2 ---- cmd BIT 9 is 0 + */ + if (adapter->pf_adapter->adapter_cnt == 4) + cmd |= 0x200; + + if (speed == RNPM_LINK_SPEED_10GB_FULL) { + cmd |= 0x2; + hw->force_speed_stat = FORCE_SPEED_STAT_10G; + } else if (speed == RNPM_LINK_SPEED_1GB_FULL) { + cmd |= 0x1; + hw->force_speed_stat = FORCE_SPEED_STAT_1G; + } else { + cmd |= 0x0; + hw->force_speed_stat = FORCE_SPEED_STAT_DISABLED; + } + + return rnpm_mbx_set_dump(hw, cmd); +} + +int rnpm_fw_update(struct rnpm_hw *hw, int partition, const u8 *fw_bin, + int bytes) +{ + int err; + struct mbx_req_cookie *cookie = NULL; + u64 address; + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + void *dma_buf = NULL; + dma_addr_t dma_phy; + + cookie = mbx_cookie_zalloc(0); + if (!cookie) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + // dma_buf = pci_alloc_consistent(hw->pdev, bytes, &dma_phy); + dma_buf = + dma_alloc_coherent(&hw->pdev->dev, bytes, &dma_phy, GFP_ATOMIC); + if (!dma_buf) { + dev_err(&hw->pdev->dev, "%s: no memory:%d!", __func__, bytes); + err = -ENOMEM; + goto quit; + } + + memcpy(dma_buf, fw_bin, bytes); + address = dma_phy; + build_fw_update_req(&req, cookie, partition, address & 0xffffffff, + (address >> 32) & 0xffffffff, bytes); + + if (hw->mbx.irq_enabled) { + cookie->timeout_jiffes = 400 * HZ; + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + } else { + int old_mbx_timeout = hw->mbx.timeout; + + hw->mbx.timeout = + (400 * 1000 * 1000) / hw->mbx.usec_delay; // wait 400s + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + hw->mbx.timeout = old_mbx_timeout; + } + +quit: + if (dma_buf) + dma_free_coherent(&hw->pdev->dev, bytes, dma_buf, dma_phy); + kfree(cookie); + + return (err) ? -EIO : 0; +} + +int rnpm_mbx_pf_link_event_enable_nolock(struct rnpm_hw *hw, int enable) +{ + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int err, v; +#define DM_MAGIC_CODE 0xa5000000 + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + if (enable) { + v = rd32(hw, RNPM_DMA_DUMY); + if (!!((v & GENMASK(31, 28)) - DM_MAGIC_CODE)) { + v &= ~GENMASK(31, 28); + v |= DM_MAGIC_CODE; + wr32(hw, RNPM_DMA_DUMY, v); + } + } else { + wr32(hw, RNPM_DMA_DUMY, 0); + } + + build_link_set_event_mask(&req, BIT(EVT_LINK_UP), + (enable & 1) << EVT_LINK_UP, &req); + + err = rnpm_mbx_write_posted_locked(hw, &req); + + return err; +} + +int rnpm_mbx_pf_link_event_enable(struct rnpm_hw *hw, int enable) +{ + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int err; + unsigned long flags; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + // dev_info(&hw->pdev->dev, "%s: lane%d %d\n", __func__, hw->nr_lane, + // enable); dump_stack(); + + if (enable) { + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + int v; + + spin_lock_irqsave(&pf_adapter->dummy_setup_lock, flags); + v = rd32(hw, RNPM_DMA_DUMY); + v &= 0x0000ffff; + v |= 0xa5a40000; + wr32(hw, RNPM_DMA_DUMY, v); + spin_unlock_irqrestore(&pf_adapter->dummy_setup_lock, flags); + } else { + wr32(hw, RNPM_DMA_DUMY, 0); + } + + build_link_set_event_mask(&req, BIT(EVT_LINK_UP), + (enable & 1) << EVT_LINK_UP, &req); + + err = rnpm_mbx_write_posted_locked(hw, &req); + + return err; +} + +int rnpm_mbx_pluginout_evt_en(struct rnpm_hw *hw, int in_dir, int enable) +{ + struct mbx_fw_cmd_req req; + int err; + + build_pluginout_evt_notify(&req, hw->nr_lane, in_dir, !!enable, &req); + + err = rnpm_mbx_write_posted_locked(hw, &req); + + return err; +} + +int rnpm_mbx_lane_link_changed_event_enable(struct rnpm_hw *hw, int enable) +{ + struct mbx_fw_cmd_req req; + int err; + + if (hw->single_lane_link_evt_ctrl_ablity == 0) + return rnpm_mbx_pf_link_event_enable(hw, enable); + + memset(&req, 0, sizeof(req)); + + build_lane_link_change_notify(&req, hw->nr_lane, !!enable, &req); + + err = rnpm_mbx_write_posted_locked(hw, &req); + + return err; +} + +int rnpm_fw_get_capablity(struct rnpm_hw *hw, struct phy_abilities *abil) +{ + int err; + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + build_phy_abalities_req(&req, &req); + + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + + if (err == 0) + memcpy(abil, &reply.phy_abilities, sizeof(*abil)); + return err; +} + +int rnpm_set_lane_fun(struct rnpm_hw *hw, int fun, int value0, int value1, + int value2, int value3) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + rnpm_logd(LOG_SET_LANE_FUN, "%s: fun:%d %d-%d-%d-%d\n", __func__, fun, + value0, value1, value2, value3); + + build_set_lane_fun(&req, hw->nr_lane, fun, value0, value1, value2, + value3); + + return rnpm_mbx_write_posted_locked(hw, &req); +} + +int rnpm_mbx_ifup_down(struct rnpm_hw *hw, int up) +{ + int err; + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + struct rnpm_adapter *adpt = hw->back; + // struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(hw->pdev); + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + build_ifup_down(&req, hw->nr_lane, up); + + rnpm_logd(LOG_MBX_IFUP_DOWN, "%s:%s lane:%d up:%d\n", __func__, + adpt->name, hw->nr_lane, up); + + if (mutex_lock_interruptible(hw->mbx.lock)) { + rnpm_err("%s: get lock failed!\n", __func__); + return -EAGAIN; + } + // if (test_bit(__RNPM_REMOVING, &pf_adapter->state)) + // return RNPM_MBX_ERR_IN_REMOVING; + + err = hw->mbx.ops.write(hw, (u32 *)&req, + (req.datalen + MBX_REQ_HDR_LEN) / 4, MBX_FW); + + mutex_unlock(hw->mbx.lock); + + // force firmware report link-status + // if (up) { + // rnpm_link_stat_mark_reset(hw); + //} + return err; +} + +int rnpm_mbx_led_set(struct rnpm_hw *hw, int value) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + + build_led_set(&req, hw->nr_lane, value, &reply); + + return rnpm_mbx_write_posted_locked(hw, &req); +} + +__always_unused static int +rnpm_nic_mode_convert_to_adapter_cnt(struct phy_abilities *ability) +{ + int adapter_cnt = 0; + + switch (ability->nic_mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + adapter_cnt = 1; + break; + case MODE_NIC_MODE_2PORT: + adapter_cnt = 2; + break; + case MODE_NIC_MODE_4PORT: + adapter_cnt = 4; + break; + default: + adapter_cnt = 0; + break; + } + + return adapter_cnt; +} + +int rnpm_mbx_get_capability(struct rnpm_hw *hw, struct rnpm_info *info) +{ + // struct rnpm_mbx_info *mbx = &hw->mbx; + int err; + struct phy_abilities ablity; + int try_cnt = 3; + + // rnpm_mbx_reset(hw); + + memset(&ablity, 0, sizeof(ablity)); + + rnpm_link_stat_mark_disable(hw); + + // enable CM3CPU to PF MBX IRQ + // wr32(hw, CPU_PF_MBOX_MASK, 0); + + while (try_cnt--) { + err = rnpm_fw_get_capablity(hw, &ablity); + if (err == 0 && info) { + hw->lane_mask = ablity.lane_mask & 0xf; + // info->mac = to_mac_type(&ablity); + info->adapter_cnt = Hamming_weight_1(hw->lane_mask); + hw->mode = ablity.nic_mode; + hw->pfvfnum = ablity.pfnum; + hw->ablity_speed = hw->speed = ablity.speed; + hw->nr_lane = 0; // PF1 + hw->fw_version = ablity.fw_version; + // hw->mac_type = info->mac; + hw->phy_type = rnpm_phy_unknown; + hw->axi_mhz = ablity.axi_mhz; + hw->port_ids = ablity.port_ids; + hw->fw_uid = ablity.fw_uid; + hw->phy.id = ablity.phy_id; + hw->wol = ablity.wol_status; + if (ablity.phy_type == PHY_TYPE_SGMII) + hw->is_sgmii = 1; + if (ablity.fw_version >= 0x00050200) + hw->single_lane_link_evt_ctrl_ablity = 1; + + if (ablity.ext_ablity != 0xffffffff && ablity.valid) { + hw->fw_lldp_ablity = ablity.fw_lldp_ablity; + hw->ncsi_en = ablity.ncsi_en; + hw->ncsi_rar_entries = 1; + hw->rpu_en = ablity.rpu_en; + if (hw->rpu_en) + ablity.rpu_availble = 1; + hw->rpu_availble = ablity.rpu_availble; + hw->max_speed_1g = ablity.only_1g; + } else { + hw->ncsi_rar_entries = 0; + } + + pr_info("%s: nic-mode:%d adpt_cnt:%d lane_mask:0x%x, ", + __func__, hw->mode, info->adapter_cnt, + hw->lane_mask); + pr_info("pfvfnum:0x%x, fw-version:0x%08x, axi:%d Mhz ", + hw->pfvfnum, ablity.fw_version, ablity.axi_mhz); + pr_info("port_id:%d-%d-%d-%d, uid:0x%08x sgmii:%d ext-ablity:0x%x", + ablity.port_id[0], ablity.port_id[1], + ablity.port_id[2], ablity.port_id[3], + hw->fw_uid, hw->is_sgmii, ablity.ext_ablity); + pr_info("ncsi:%u wol=0x%x rpu:%d-%d only-1g:%d\n", + hw->ncsi_en & 1, hw->wol, hw->rpu_en, + hw->rpu_availble, hw->max_speed_1g); + if (info->adapter_cnt > 0) + return 0; + } + } + + dev_err(&hw->pdev->dev, "%s: error!\n", __func__); + + return -EIO; +} + +int rnpm_mbx_get_temp(struct rnpm_hw *hw, int *voltage) +{ + int err; + struct mbx_req_cookie *cookie = NULL; + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + struct get_temp *temp; + int temp_v = 0; + + cookie = mbx_cookie_zalloc(sizeof(*temp)); + if (!cookie) + return -ENOMEM; + temp = (struct get_temp *)cookie->priv; + + memset(&req, 0, sizeof(req)); + + build_get_temp(&req, cookie); + + if (hw->mbx.irq_enabled) { + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + } else { + memset(&reply, 0, sizeof(reply)); + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + temp = &reply.get_temp; + } + // printk("%s: err:%d\n", __func__, err); + + if (voltage) + *voltage = temp->volatage; + temp_v = temp->temp; + + kfree(cookie); + return temp_v; +} +int rnpm_fw_reg_read(struct rnpm_hw *hw, int addr, int sz) +{ + struct mbx_req_cookie *cookie; + struct mbx_fw_cmd_req req; + int value; + + cookie = mbx_cookie_zalloc(sizeof(int)); + if (!cookie) + return -ENOMEM; + + build_readreg_req(&req, addr, cookie); + + rnpm_mbx_fw_post_req(hw, &req, cookie); + + value = *((int *)cookie->priv); + kfree(cookie); + return 0; +} + +void rnpm_link_stat_mark(struct rnpm_hw *hw, int nr_lane, int up) +{ + u32 v; + struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + unsigned long flags; + + spin_lock_irqsave(&pf_adapter->dummy_setup_lock, flags); + v = rd32(hw, RNPM_DMA_DUMY); + v &= ~(0xffff0000); + v |= 0xa5a40000; + if (up) + v |= BIT(nr_lane); + else + v &= ~BIT(nr_lane); + wr32(hw, RNPM_DMA_DUMY, v); + spin_unlock_irqrestore(&pf_adapter->dummy_setup_lock, flags); +} + +void rnpm_mbx_probe_stat_set(struct rnpm_pf_adapter *pf_adapter, int stat) +{ +#define RNPM_DMA_DUMMY_PROBE_STAT_BIT (4) + unsigned long flags; + struct rnpm_hw *hw = &pf_adapter->hw; + u32 v; + + spin_lock_irqsave(&pf_adapter->dummy_setup_lock, flags); + v = rd32(hw, RNPM_DMA_DUMY); + v &= ~(0xffff0000); + v |= 0xa5a40000; + if (stat == MBX_PROBE) + v |= BIT(RNPM_DMA_DUMMY_PROBE_STAT_BIT); + else if (stat == MBX_REMOVE) + v = 0xFFA5A6A7; + else + v &= ~BIT(RNPM_DMA_DUMMY_PROBE_STAT_BIT); + wr32(hw, RNPM_DMA_DUMY, v); + spin_unlock_irqrestore(&pf_adapter->dummy_setup_lock, flags); +} + +int rnpm_hw_set_fw_10g_1g_auto_detch(struct rnpm_hw *hw, int enable) +{ + return rnpm_mbx_set_dump(hw, 0x01140000 | (enable & 1)); +} + +int rnpm_hw_set_clause73_autoneg_enable(struct rnpm_hw *hw, int enable) +{ + return rnpm_mbx_set_dump(hw, 0x010e0000 | (enable & 1)); +} + +static inline int rnpm_mbx_fw_req_handler(struct rnpm_pf_adapter *adapter, + struct mbx_fw_cmd_req *req) +{ + struct rnpm_hw *hw; + // u32 value; + int i, nr_lane; + struct rnpm_adapter *adpt; + struct port_stat *st; + + switch (req->opcode) { + case PTP_EVENT: { + rnpm_logd(LOG_PTP_EVT, "ptp event:lanes:0x%x\n", + req->ptp.lanes); + rnpm_info("%s:ptp event:lanes:0x%x\n", __func__, + req->ptp.lanes); + break; + } + case PLUG_EVENT: { + for (i = 0; i < adapter->adapter_cnt; i++) { + if (!rnpm_port_is_valid(adapter, i)) + continue; + + adpt = adapter->adapter[i]; + if (adpt == NULL) + continue; + hw = &adpt->hw; + nr_lane = adpt->hw.nr_lane & 0b11; + + if (nr_lane == req->plugin_out.nr_lane) { + rnpm_info("%s plu%s\n", adpt->name, + req->plugin_out.action ? "out" : + "in"); + break; + } + } + break; + } + case LINK_STATUS_EVENT: + rnpm_logd( + LOG_LINK_EVENT, + "link changed: change lane:0x%x status:0x%x speed:%d-%d-%d-%d\n", + req->link_stat.changed_lanes, + req->link_stat.lane_status, req->link_stat.st[0].speed, + req->link_stat.st[1].speed, req->link_stat.st[2].speed, + req->link_stat.st[3].speed); + + for (i = 0; i < adapter->adapter_cnt; i++) { + if (!rnpm_port_is_valid(adapter, i)) + continue; + + adpt = adapter->adapter[i]; + if (adpt == NULL) + continue; + hw = &adpt->hw; + nr_lane = adpt->hw.nr_lane & 0b11; + + if (BIT(nr_lane) & + req->link_stat.lane_status) { // linkup + adpt->hw.link = 1; + // rnpm_link_stat_mark(&adpt->hw, nr_lane, 1); + } else { + adpt->hw.link = 0; + // rnpm_link_stat_mark(&adpt->hw, nr_lane, 0); + } + if ((BIT(i) & req->link_stat.changed_lanes) && + req->link_stat.port_st_magic == SPEED_VALID_MAGIC) { + st = &req->link_stat.st[nr_lane]; + adpt->speed = st->speed; + adpt->phy_addr = st->phy_addr; + adpt->an = st->autoneg ? true : false; + adpt->duplex = st->duplex; + adpt->flags |= RNPM_FLAG_NEED_LINK_UPDATE; + } + + rnpm_logd( + LOG_LINK_EVENT, + "%s:%s:lane:%d link:%d speed:%d hw:%p phy_addr:0x%x\n", + __func__, adpt->name, nr_lane, adpt->hw.link, + adpt->speed, &adpt->hw, adpt->phy_addr); + + // rnpm_service_event_schedule(adpt); + } + set_bit(RNPM_PF_LINK_CHANGE, &adapter->flags); + + break; + } + + return 0; +} + +static inline int rnpm_mbx_fw_reply_handler(struct rnpm_pf_adapter *adapter, + struct mbx_fw_cmd_reply *reply) +{ + struct mbx_req_cookie *cookie; + + // buf_dump("reply:", reply, sizeof(*reply)); + + cookie = reply->cookie; + if (!cookie || cookie->magic != COOKIE_MAGIC) + return -EIO; + + if (cookie->priv_len > 0) + memcpy(cookie->priv, reply->data, cookie->priv_len); + cookie->done = 1; + + if (reply->flags & FLAGS_ERR) + cookie->errcode = reply->error_code; + else + cookie->errcode = 0; + wake_up_interruptible(&cookie->wait); + + return 0; +} + +static inline int rnpm_rcv_msg_from_fw(struct rnpm_pf_adapter *adapter) +{ + u32 msgbuf[RNP_FW_MAILBOX_SIZE]; + struct rnpm_hw *hw = &adapter->hw; + s32 retval; + + retval = rnpm_read_mbx(hw, msgbuf, RNP_FW_MAILBOX_SIZE, MBX_FW); + if (retval) { + rnpm_err("Error receiving message from FW:#%d ret:%d\n", + __LINE__, retval); + return retval; + } + + rnpm_logd(LOG_MBX_MSG_IN, "msg[0]=0x%08x_0x%08x_0x%08x_0x%08x\n", + msgbuf[0], msgbuf[1], msgbuf[2], msgbuf[3]); + + /* this is a message we already processed, do nothing */ + if (((unsigned short *)msgbuf)[0] & FLAGS_DD) { // + return rnpm_mbx_fw_reply_handler( + adapter, (struct mbx_fw_cmd_reply *)msgbuf); + } else { // req + return rnpm_mbx_fw_req_handler(adapter, + (struct mbx_fw_cmd_req *)msgbuf); + } +} + +static void rnpm_rcv_ack_from_fw(struct rnpm_pf_adapter *adapter) +{ +} + +int rnpm_fw_msg_handler(struct rnpm_pf_adapter *pf_adapter) +{ + // == check cpureq + if (!rnpm_check_for_msg(&pf_adapter->hw, MBX_FW)) + rnpm_rcv_msg_from_fw(pf_adapter); + + /* process any acks */ + if (!rnpm_check_for_ack(&pf_adapter->hw, MBX_FW)) + rnpm_rcv_ack_from_fw(pf_adapter); + + return 0; +} + +int rnpm_mbx_phy_write(struct rnpm_hw *hw, u32 reg, u32 val) +{ + struct mbx_fw_cmd_req req; + char nr_lane = hw->nr_lane; + + memset(&req, 0, sizeof(req)); + + build_set_phy_reg(&req, NULL, PHY_EXTERNAL_PHY_MDIO, nr_lane, reg, val, + 0); + + return rnpm_mbx_write_posted_locked(hw, &req); +} + +int rnpm_mbx_phy_read(struct rnpm_hw *hw, u32 reg, u32 *val) +{ + struct mbx_fw_cmd_req req; + int err = -EIO; + char nr_lane = hw->nr_lane; + + memset(&req, 0, sizeof(req)); + + // dump_stack(); + + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = mbx_cookie_zalloc(4); + + if (!cookie) + return -ENOMEM; + build_get_phy_reg(&req, cookie, PHY_EXTERNAL_PHY_MDIO, nr_lane, + reg); + + err = rnpm_mbx_fw_post_req(hw, &req, cookie); + if (err) { + kfree(cookie); + return err; + } + memcpy(val, cookie->priv, 4); + err = 0; + kfree(cookie); + } else { + struct mbx_fw_cmd_reply reply; + + memset(&reply, 0, sizeof(reply)); + build_get_phy_reg(&req, &reply, PHY_EXTERNAL_PHY_MDIO, nr_lane, + reg); + + err = rnpm_fw_send_cmd_wait(hw, &req, &reply); + if (err == 0) + *val = reply.r_reg.value[0]; + } + return err; +} + +int rnpm_mbx_phy_link_set(struct rnpm_hw *hw, int speeds) +{ + struct mbx_fw_cmd_req req; + + memset(&req, 0, sizeof(req)); + + rnpm_info("%s:lane:%d speed:0x%x\n", __func__, hw->nr_lane, speeds); + + build_phy_link_set(&req, speeds, hw->nr_lane); + + return rnpm_mbx_write_posted_locked(hw, &req); +} + +int rnpm_get_port_stats2(struct rnpm_hw *hw, int *pabs, int *pspeed, int *plink) +{ + unsigned int v; + int link = 0, abs = 0, idx = 0, speed = 0; + int speed_tb[6] = { 10, 100, 1000, 10 * 1000, 25 * 1000, 40 * 1000 }; + +#define LANES_STAT (0xa8000 + 64 * 64 - 4) + v = rd32(hw, LANES_STAT); + + if ((v & 0xF0000000) != 0xA0000000) + return -1; + + link = !!(v & BIT(hw->nr_lane)); + abs = !!(v & BIT(hw->nr_lane + 4)); + idx = (v >> 8) & 0xf; + if ((idx <= 5)) + speed = speed_tb[idx]; + if (plink) + *plink = link; + if (pspeed) + *pspeed = speed; + if (pabs) + *pabs = abs; + + // printk("%s lane%d link%d speed:%d pabs:%d\n", __func__, hw->nr_lane, link, speed, abs); + + return link; +} + +int rnpm_mbx_sdram_simm(struct rnpm_hw *hw, u32 flags, u32 offset, + void *dma_buf, dma_addr_t dma_phy, u32 len) +{ + int err; + u64 address; + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + + if (!dma_buf || dma_phy == 0) { + dev_err(&hw->pdev->dev, "%s: no memory:%d!", __func__, len); + return -ENOMEM; + } + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + address = dma_phy; + build_comm_sdram_req(&req, NULL, flags, offset, address & 0xffffffff, + (address >> 32) & 0xffffffff, len); + if (hw->mbx.irq_enabled) + rnpm_mbx_write_posted_locked(hw, &req); + + return err ? -err : 0; +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.h b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.h new file mode 100644 index 000000000000..fba1092276b4 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mbx_fw.h @@ -0,0 +1,1201 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef MBX_FW_CMD_H +#define MBX_FW_CMD_H + +#include +#include +#include + +#ifndef _PACKED_ALIGN4 +#define _PACKED_ALIGN4 __attribute__((packed, aligned(4))) +#endif + +#define RNPM_MBX_ERR_IN_REMOVING (-200) + +struct mbx_fw_cmd_reply; +typedef void (*cookie_cb)(struct mbx_fw_cmd_reply *reply, void *priv); + +struct mbx_req_cookie { + int magic; +#define COOKIE_MAGIC 0xCE + cookie_cb cb; + int timeout_jiffes; + int errcode; + wait_queue_head_t wait; + int done; + int priv_len; + char priv[]; +}; + +enum GENERIC_CMD { + /* generat */ + GET_VERSION = 0x0001, + READ_REG = 0xFF03, + WRITE_REG = 0xFF04, + MODIFY_REG = 0xFF07, + + /* virtualization */ + IFUP_DOWN = 0x0800, + PTP_EVENT = 0x0801, + DRIVER_INSMOD = 0x0803, + SYSTEM_SUSPUSE = 0x0804, + + /* link configuration admin commands */ + GET_PHY_ABALITY = 0x0601, + GET_MAC_ADDRES = 0x0602, + RESET_PHY = 0x0603, + LED_SET = 0x0604, + GET_LINK_STATUS = 0x0607, + LINK_STATUS_EVENT = 0x0608, + SET_LANE_FUN = 0x0609, + GET_LANE_STATUS = 0x0610, + SFP_SPEED_CHANGED_EVENT = 0x0611, + SET_PF_EVENT_MASK = 0x0613, + SET_LANE_EVENT_EN = 0x0614, + SET_LOOPBACK_MODE = 0x0618, + PLUG_EVENT = 0x0620, + SET_PHY_REG = 0x0628, + GET_PHY_REG = 0x0629, + PHY_LINK_SET = 0x0630, + GET_PHY_STATISTICS = 0x0631, + + /*sfp-module*/ + SFP_MODULE_READ = 0x0900, + SFP_MODULE_WRITE = 0x0901, + + /* fw update */ + FW_UPDATE = 0x0700, + FW_MAINTAIN = 0x0701, + EMI_SYNC = 0x0706, + + WOL_EN = 0x0910, + GET_DUMP = 0x0a00, + SET_DUMP = 0x0a10, + GET_TEMP = 0x0a11, + SET_WOL = 0x0a12, + + LLDP_TX_CTL = 0x0a13, +}; + +enum link_event_mask { + EVT_LINK_UP = 1, + EVT_NO_MEDIA = 2, + EVT_LINK_FAULT = 3, + EVT_PHY_TEMP_ALARM = 4, + EVT_EXCESSIVE_ERRORS = 5, + EVT_SIGNAL_DETECT = 6, + EVT_AUTO_NEGOTIATION_DONE = 7, + EVT_MODULE_QUALIFICATION_FAILD = 8, + EVT_PORT_TX_SUSPEND = 9, +}; + +#define PMA_UNKNOWN 0b111111 // 0x3f +#define PMA_1000BASE_KX 0b001101 +#define PMA_1000BASE_T 0b001100 +#define PMA_10GBASE_KR 0b001011 +#define PMA_10GBASE_KX4 0b001010 +#define PMA_10GBASE_T 0b001001 +#define PMA_10GBASE_LRM 0b001000 +#define PMA_10GBASE_SR 0b000111 +#define PMA_10GBASE_LR 0b000110 +#define PMA_10GBASE_ER 0b000101 +#define PMA_10GBASE_LX4 0b000100 +#define PMA_10GBASE_SW 0b000011 +#define PMA_10GBASE_LW 0b000010 +#define PMA_10GBASE_EW 0b000001 +#define PMA_10GBASE_CX4 0b000000 +#define PMA_5BASE_KR 0b111100 +#define PMA_2_5GBASE_KX 0b111011 +#define PMA_40GBASE_ER4 0b100101 +#define PMA_40GBASE_FR 0b100100 +#define PMA_40GBASE_LR4 0b100011 +#define PMA_40GBASE_SR4 0b100010 +#define PMA_40GBASE_CR4 0b100001 +#define PMA_40GBASE_KR4 0b100000 + +enum phy_type { + PHY_TYPE_NONE = 0, + PHY_TYPE_1G_BASE_KX, + PHY_TYPE_SGMII, + PHY_TYPE_10G_BASE_KR, + PHY_TYPE_25G_BASE_KR, + PHY_TYPE_40G_BASE_KR4, + PHY_TYPE_10G_BASE_SR, + PHY_TYPE_40G_BASE_SR4, + PHY_TYPE_40G_BASE_CR4, + PHY_TYPE_40G_BASE_LR4, + PHY_TYPE_10G_BASE_LR, + PHY_TYPE_10G_BASE_ER, +}; + +struct phy_abilities { + unsigned char link_stat; + unsigned char lane_mask; + + int speed; + short phy_type; + short nic_mode; + short pfnum; + unsigned int fw_version; + unsigned int axi_mhz; + union { + unsigned char port_id[4]; + unsigned int port_ids; + }; + unsigned int fw_uid; + unsigned int phy_id; + int wol_status; + + union { + unsigned int ext_ablity; + struct { + unsigned int valid : 1; /* 0 */ + unsigned int wol_en : 1; /* 1 */ + unsigned int pci_preset_runtime_en : 1; /* 2 */ + unsigned int smbus_en : 1; /* 3 */ + unsigned int ncsi_en : 1; /* 4 */ + unsigned int rpu_en : 1; /* 5 */ + unsigned int v2 : 1; /* 6 */ + unsigned int pxe_en : 1; /* 7 */ + unsigned int mctp_en : 1; /* 8 */ + unsigned int yt8614 : 1; /* 9 */ + unsigned int pci_ext_reset : 1; /* 10 */ + unsigned int rpu_availble : 1; /* 11 */ + unsigned int fw_lldp_ablity : 1; /* 12 */ + unsigned int lldp_enabled : 1; /* 13 */ + unsigned int only_1g : 1; /* 14 */ + }; + }; +} _PACKED_ALIGN4; + +enum LOOPBACK_LEVEL { + LOOPBACK_DISABLE = 0, + LOOPBACK_MAC = 1, + LOOPBACK_PCS = 5, + LOOPBACK_EXTERNAL = 6, +}; +enum LOOPBACK_TYPE { + /* Tx->Rx */ + LOOPBACK_TYPE_LOCAL = 0x0, +}; + +enum LOOPBACK_FORCE_SPEED { + LOOPBACK_FORCE_SPEED_NONE = 0x0, + LOOPBACK_FORCE_SPEED_1GBS = 0x1, + LOOPBACK_FORCE_SPEED_10GBS = 0x2, + LOOPBACK_FORCE_SPEED_40_25GBS = 0x3, +}; + +enum PHY_INTERFACE { + PHY_INTERNAL_PHY = 0, + PHY_EXTERNAL_PHY_MDIO = 1, +}; + +/* Table 3-54. Get link status response (opcode: 0x0607) */ +struct link_stat_data { + char phy_type; + unsigned char speed; +#define LNK_STAT_SPEED_UNKOWN 0 +#define LNK_STAT_SPEED_10 1 +#define LNK_STAT_SPEED_100 2 +#define LNK_STAT_SPEED_1000 3 +#define LNK_STAT_SPEED_10000 4 +#define LNK_STAT_SPEED_25000 5 +#define LNK_STAT_SPEED_40000 6 + + /* 2 */ + char link_stat : 1; +#define LINK_UP 1 +#define LINK_DOWN 0 + + char link_fault : 4; +#define LINK_LINK_FAULT BIT(0) +#define LINK_TX_FAULT BIT(1) +#define LINK_RX_FAULT BIT(2) +#define LINK_REMOTE_FAULT BIT(3) + + char extern_link_stat : 1; + char media_availble : 1; + + char rev1 : 1; + + /* 3:ignore */ + char an_completed : 1; + char lp_an_ablity : 1; + char parallel_detection_fault : 1; + char fec_enabled : 1; + char low_power_state : 1; + char link_pause_status : 2; + char qualified_odule : 1; + + /* 4 */ + char phy_temp_alarm : 1; + char excessive_link_errors : 1; + char port_tx_suspended : 2; + char force_40G_enabled : 1; + char external_25G_phy_err_code : 3; +#define EXTERNAL_25G_PHY_NOT_PRESENT 1 +#define EXTERNAL_25G_PHY_NVM_CRC_ERR 2 +#define EXTERNAL_25G_PHY_MDIO_ACCESS_FAILD 6 +#define EXTERNAL_25G_PHY_INIT_SUCCED 7 + + /* 5 */ + char loopback_enabled_status : 4; +#define LOOPBACK_DISABLE 0x0 +#define LOOPBACK_MAC 0x1 +#define LOOPBACK_SERDES 0x2 +#define LOOPBACK_PHY_INTERNAL 0x3 +#define LOOPBACK_PHY_EXTERNAL 0x4 + char loopback_type_status : 1; +#define LOCAL_LOOPBACK 0 /* tx->rx */ +#define FAR_END_LOOPBACK 0 /* rx->Tx */ + char rev3 : 1; + char external_dev_power_ability : 2; + /* 6-7 */ + short max_frame_sz; + /* 8 */ + char _25gb_kr_fec_enabled : 1; + char _25gb_rs_fec_enabled : 1; + char crc_enabled : 1; + char rev4 : 5; + /* 9 */ + int link_type; /* same as Phy type */ + char link_type_ext; +} _PACKED_ALIGN4; + +struct port_stat { + u8 phy_addr; + + u8 duplex : 1; + u8 autoneg : 1; + u8 fec : 1; + u32 speed; +} __packed; + +struct lane_stat_data { + u8 nr_lane; + u8 pci_gen : 4; + u8 pci_lanes : 4; + u8 pma_type; + u8 phy_type; + + u16 linkup : 1; + u16 duplex : 1; + u16 autoneg : 1; + u16 fec : 1; + u16 an : 1; + u16 link_traing : 1; + u16 media_availble : 1; // + u16 is_sgmii : 1; // + u16 link_fault : 4; +#define LINK_LINK_FAULT BIT(0) +#define LINK_TX_FAULT BIT(1) +#define LINK_RX_FAULT BIT(2) +#define LINK_REMOTE_FAULT BIT(3) + u16 is_backplane : 1; + u16 tp_mdx : 2; + + union { + u8 phy_addr; + struct { + u8 mod_abs : 1; + u8 fault : 1; + u8 tx_dis : 1; + u8 los : 1; + } sfp; + }; + u8 sfp_connector; + u32 speed; + + u32 si_main; + u32 si_pre; + u32 si_post; + u32 si_tx_boost; + u32 supported_link; + u32 phy_id; + u32 advertised_link; +} __packed; + +struct yt_phy_statistics { + u32 pkg_ib_valid; /* rx crc good and length 64-1518 */ + u32 pkg_ib_os_good; /* rx crc good and length >1518 */ + u32 pkg_ib_us_good; /* rx crc good and length <64 */ + u16 pkg_ib_err; /* rx crc wrong and length 64-1518 */ + u16 pkg_ib_os_bad; /* rx crc wrong and length >1518 */ + u16 pkg_ib_frag; /* rx crc wrong and length <64 */ + u16 pkg_ib_nosfd; /* rx sfd missed */ + u32 pkg_ob_valid; /* tx crc good and length 64-1518 */ + u32 pkg_ob_os_good; /* tx crc good and length >1518 */ + u32 pkg_ob_us_good; /* tx crc good and length <64 */ + u16 pkg_ob_err; /* tx crc wrong and length 64-1518 */ + u16 pkg_ob_os_bad; /* tx crc wrong and length >1518 */ + u16 pkg_ob_frag; /* tx crc wrong and length <64 */ + u16 pkg_ob_nosfd; /* tx sfd missed */ +} __packed; + +struct phy_statistics { + union { + struct yt_phy_statistics yt; + }; +} __packed; +/* == flags == */ +#define FLAGS_DD BIT(0) /* driver clear 0, FW must set 1 */ +#define FLAGS_CMP BIT(1) /* driver clear 0, FW mucst set */ +#define FLAGS_ERR \ + BIT(2) /* driver clear 0, FW must set only if it reporting an error */ +#define FLAGS_LB BIT(9) +#define FLAGS_RD BIT(10) /* set if additional buffer has command parameters */ +#define FLAGS_BUF BIT(12) /* set 1 on indirect command */ +#define FLAGS_SI BIT(13) /* not irq when command complete */ +#define FLAGS_EI BIT(14) /* interrupt on error */ +#define FLAGS_FE BIT(15) /* flush erro */ + +#ifndef SHM_DATA_MAX_BYTES +#define SHM_DATA_MAX_BYTES (64 - 2 * 4) +#endif + +#define MBX_REQ_HDR_LEN 24 +#define MBX_REPLYHDR_LEN 16 +#define MBX_REQ_MAX_DATA_LEN (SHM_DATA_MAX_BYTES - MBX_REQ_HDR_LEN) +#define MBX_REPLY_MAX_DATA_LEN (SHM_DATA_MAX_BYTES - MBX_REPLYHDR_LEN) + +// TODO req is little endian. bigendian should be conserened + +struct mbx_fw_cmd_req { + unsigned short flags; /* 0-1 */ + unsigned short opcode; /* 2-3 enum LINK_ADM_CMD */ + unsigned short datalen; /* 4-5 */ + unsigned short ret_value; /* 6-7 */ + union { + struct { + unsigned int cookie_lo; /* 8-11 */ + unsigned int cookie_hi; /* 12-15 */ + }; + void *cookie; + }; + unsigned int reply_lo; // 16-19 5dw + unsigned int reply_hi; // 20-23 + /*=== data === 7dw [24-64] */ + union { + char data[0]; + + struct { + unsigned int addr; + unsigned int bytes; + } r_reg; + + struct { + unsigned int addr; + unsigned int bytes; + unsigned int data[4]; + } w_reg; + + struct { + unsigned int lanes; + } ptp; + + struct { + int lane; + int up; + } ifup; + + struct { + int nr_lane; +#define LLDP_TX_ALL_LANES 0xFF + int op; +#define LLDP_TX_SET 0x0 +#define LLDP_TX_GET 0x1 + int enable; + } lldp_tx; + + struct { + int lane; + int status; + } ifinsmod; + + struct { + int lane; + int status; + } ifsuspuse; + + struct { + int nr_lane; + } get_lane_st; + + struct { + int nr_lane; + int func; +#define LANE_FUN_AN 0 +#define LANE_FUN_LINK_TRAING 1 +#define LANE_FUN_FEC 2 +#define LANE_FUN_SI 3 +#define LANE_FUN_SFP_TX_DISABLE 4 +#define LANE_FUN_PCI_LANE 5 +#define LANE_FUN_PRBS 6 +#define LANE_FUN_SPEED_CHANGE 7 + + int value0; + int value1; + int value2; + int value3; + } set_lane_fun; + + struct { + int flag; + int nr_lane; + } set_dump; + + struct { + int lane; + int enable; + } wol; + + struct { + unsigned int bytes; + unsigned int nr_lane; + unsigned int bin_phy_lo; + unsigned int bin_phy_hi; + } get_dump; + + struct { + unsigned int nr_lane; + int value; +#define LED_IDENTIFY_INACTIVE 0 +#define LED_IDENTIFY_ACTIVE 1 +#define LED_IDENTIFY_ON 2 +#define LED_IDENTIFY_OFF 3 + } led_set; + + struct { + unsigned int addr; + unsigned int data; + unsigned int mask; + } modify_reg; + + struct { + unsigned int adv_speed_mask; + int nr_lane; + } phy_link_set; + + struct { + unsigned int pause_mode; + int nr_lane; + } phy_pause_set; + + struct { + unsigned int nr_lane; + unsigned int sfp_adr; // 0xa0 or 0xa2 + unsigned int reg; + unsigned int cnt; + } sfp_read; + + struct { + unsigned int nr_lane; + unsigned int sfp_adr; // 0xa0 or 0xa2 + unsigned int reg; + unsigned int val; + } sfp_write; + + struct { + unsigned int nr_lane; /*0-3*/ + } get_linkstat; + struct { + unsigned short changed_lanes; + unsigned short lane_status; + unsigned int port_st_magic; +#define SPEED_VALID_MAGIC 0xa4a6a8a9 + struct port_stat st[4]; + } link_stat; // FW->RC + + struct plugin_s { + int nr_lane; + int action; +#define PLUGIN_ACT_IN 0 +#define PLUGIN_ACT_OFF 1 + } plugin_out; // FW -> RC + + struct { + unsigned short enable_stat; + unsigned short event_mask; // enum link_event_mask + } stat_pf_event_mask; + + struct stat_lane_evt_t { + int nr_lane; + union { + u8 v; + struct { + u8 link_change : 1; + u8 plugin : 1; + u8 plugout : 1; + }; + } evt; + union { + u8 v; + struct { + u8 link_change_en : 1; + u8 plugin_en : 1; + u8 plugout_en : 1; + }; + } value; + } stat_lane_evt; + + struct { /* set loopback */ + unsigned char loopback_level; + unsigned char loopback_type; + unsigned char loopback_force_speed; + + char loopback_force_speed_enable : 1; + } loopback; + + struct { + int cmd; + int arg0; + int req_bytes; + int reply_bytes; + int ddr_lo; + int ddr_hi; + } maintain; + + struct { /* set phy register */ + char phy_interface; + union { + char page_num; + char external_phy_addr; + }; + int phy_reg_addr; + int phy_w_data; + int reg_addr; + int w_data; + /* 1 = ignore page_num, use last QSFP */ + char recall_qsfp_page : 1; + /* page value */ + /* 0 = use page_num for QSFP */ + char nr_lane; + } set_phy_reg; + struct { + } get_phy_ablity; + + struct { + int lane_mask; + int pfvf_num; + } get_mac_addr; + + struct { + char phy_interface; + union { + char page_num; + char external_phy_addr; + }; + int phy_reg_addr; + char nr_lane; + } get_phy_reg; + + struct { + unsigned int nr_lane; + } phy_statistics; + + struct { + char paration; + unsigned int bytes; + unsigned int bin_phy_lo; + unsigned int bin_phy_hi; + } fw_update; + + struct emi_sync_st { + unsigned int offset; + unsigned int bytes; + unsigned int bin_phy_lo; + unsigned int bin_phy_hi; + unsigned int op; + } emi_sync; + }; +} _PACKED_ALIGN4; + +/* firmware -> driver */ +struct mbx_fw_cmd_reply { + unsigned short flags; // fw must set: DD, CMP, Error(if error), copy value + // from command: LB,RD,VFC,BUF,SI,EI,FE + unsigned short opcode; // 2-3: copy from req + unsigned short error_code; // 4-5: 0 if no error + unsigned short datalen; // 6-7: + union { + struct { + unsigned int cookie_lo; // 8-11: + unsigned int cookie_hi; // 12-15: + }; + void *cookie; + }; + //===== data ==== [16-64] + union { + char data[0]; + + struct version { // GET_VERSION + unsigned int major; + unsigned int sub; + unsigned int modify; + } version; + + struct { + unsigned int value[4]; + } r_reg; + + struct { + unsigned int new_value; + } modify_reg; + + struct get_temp { + int temp; + int volatage; + } get_temp; + + struct lldp_stat { + int enable_stat; + } lldp; + + struct { +#define MBX_SFP_READ_MAX_CNT 32 + char value[MBX_SFP_READ_MAX_CNT]; + } sfp_read; + + struct mac_addr { + int lanes; + struct _addr { + /* for macaddr:01:02:03:04:05:06 + * mac-hi=0x01020304 mac-lo=0x05060000 + */ + unsigned char mac[8]; + } addrs[4]; + unsigned int ccode; + } mac_addr; + + struct get_dump_reply { + int flags; + int version; + int bytes; + int data[4]; + } get_dump; + + struct lane_stat_data lanestat; + struct link_stat_data linkstat; + struct phy_abilities phy_abilities; + struct phy_statistics phy_statistics; + }; +} _PACKED_ALIGN4; + +static inline void build_maintain_req(struct mbx_fw_cmd_req *req, void *cookie, + int cmd, int arg0, int req_bytes, + int reply_bytes, u32 dma_phy_lo, + u32 dma_phy_hi) +{ + req->flags = 0; + req->opcode = FW_MAINTAIN; + req->datalen = sizeof(req->maintain); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->maintain.cmd = cmd; + req->maintain.arg0 = arg0; + req->maintain.req_bytes = req_bytes; + req->maintain.reply_bytes = reply_bytes; + req->maintain.ddr_lo = dma_phy_lo; + req->maintain.ddr_hi = dma_phy_hi; +} + +static inline void build_fw_update_req(struct mbx_fw_cmd_req *req, void *cookie, + int partition, u32 fw_bin_phy_lo, + u32 fw_bin_phy_hi, int fw_bytes) +{ + req->flags = 0; + req->opcode = FW_UPDATE; + req->datalen = sizeof(req->fw_update); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->fw_update.paration = partition; + req->fw_update.bytes = fw_bytes; + req->fw_update.bin_phy_lo = fw_bin_phy_lo; + req->fw_update.bin_phy_hi = fw_bin_phy_hi; +} + +static inline void build_reset_phy_req(struct mbx_fw_cmd_req *req, void *cookie) +{ + req->flags = 0; + req->opcode = RESET_PHY; + req->datalen = 0; + req->reply_lo = 0; + req->reply_hi = 0; + req->cookie = cookie; +} + +static inline void build_phy_abalities_req(struct mbx_fw_cmd_req *req, + void *cookie) +{ + req->flags = 0; + req->opcode = GET_PHY_ABALITY; + req->datalen = 0; + req->reply_lo = 0; + req->reply_hi = 0; + req->cookie = cookie; +} + +static inline void build_get_macaddress_req(struct mbx_fw_cmd_req *req, + int lane_mask, int pfvfnum, + void *cookie) +{ + req->flags = 0; + req->opcode = GET_MAC_ADDRES; + req->datalen = sizeof(req->get_mac_addr); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + + req->get_mac_addr.lane_mask = lane_mask; + req->get_mac_addr.pfvf_num = pfvfnum; +} + +static inline void build_version_req(struct mbx_fw_cmd_req *req, void *cookie) +{ + req->flags = 0; + req->opcode = GET_VERSION; + req->reply_lo = 0; + req->reply_hi = 0; + req->datalen = 0; + req->cookie = cookie; +} + +// 7.10.11.8 Read egister admin command +static inline void build_readreg_req(struct mbx_fw_cmd_req *req, int reg_addr, + void *cookie) +{ + req->flags = 0; + req->opcode = READ_REG; + req->datalen = sizeof(req->r_reg); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->r_reg.addr = reg_addr & ~3; + req->r_reg.bytes = 4; +} + +static inline void build_mbx_wol_set(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, u32 mode) +{ + req->flags = 0; + req->opcode = SET_WOL; + req->datalen = sizeof(req->sfp_write); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->wol.lane = nr_lane; + req->wol.enable = mode; +} + +static inline void mbx_fw_req_set_reply(struct mbx_fw_cmd_req *req, + dma_addr_t reply) +{ + u64 address = reply; + + req->reply_hi = (address >> 32); + req->reply_lo = (address) & 0xffffffff; +} + +// 7.10.11.9 Write egister admin command +static inline void build_writereg_req(struct mbx_fw_cmd_req *req, void *cookie, + int reg_addr, int bytes, int value[4]) +{ + int i; + + req->flags = 0; + req->opcode = WRITE_REG; + req->datalen = sizeof(req->w_reg); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->w_reg.addr = reg_addr & ~3; + req->w_reg.bytes = bytes; + for (i = 0; i < bytes / 4; i++) + req->w_reg.data[i] = value[i]; +} + +/* 7.10.11.10 modify egister admin command */ +static inline void build_modifyreg_req(struct mbx_fw_cmd_req *req, void *cookie, + int reg_addr, int value, + unsigned int mask) +{ + req->flags = 0; + req->opcode = MODIFY_REG; + req->datalen = sizeof(req->modify_reg); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->modify_reg.addr = reg_addr; + req->modify_reg.data = value; + req->modify_reg.mask = mask; +} + +static inline void build_get_lane_status_req(struct mbx_fw_cmd_req *req, + int nr_lane, void *cookie) +{ + req->flags = 0; + req->opcode = GET_LANE_STATUS; + req->datalen = sizeof(req->get_lane_st); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->get_lane_st.nr_lane = nr_lane; +} + +static inline void build_get_link_status_req(struct mbx_fw_cmd_req *req, + int nr_lane, void *cookie) +{ + req->flags = 0; + req->opcode = GET_LINK_STATUS; + req->datalen = sizeof(req->get_linkstat); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->get_linkstat.nr_lane = nr_lane; +} + +static inline void build_get_temp(struct mbx_fw_cmd_req *req, void *cookie) +{ + req->flags = 0; + req->opcode = GET_TEMP; + req->datalen = 0; + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; +} +static inline void build_get_dump_req(struct mbx_fw_cmd_req *req, void *cookie, + int nr_lane, u32 fw_bin_phy_lo, + u32 fw_bin_phy_hi, int bytes) +{ + req->flags = 0; + req->opcode = GET_DUMP; + req->datalen = sizeof(req->get_dump); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->get_dump.bytes = bytes; + req->get_dump.nr_lane = nr_lane; + req->get_dump.bin_phy_lo = fw_bin_phy_lo; + req->get_dump.bin_phy_hi = fw_bin_phy_hi; +} + +static inline void build_set_dump(struct mbx_fw_cmd_req *req, int nr_lane, + int flag) +{ + req->flags = 0; + req->opcode = SET_DUMP; + req->datalen = sizeof(req->set_dump); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->set_dump.flag = flag; + req->set_dump.nr_lane = nr_lane; +} + +static inline void build_led_set(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int value, void *cookie) +{ + req->flags = 0; + req->opcode = LED_SET; + req->datalen = sizeof(req->led_set); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->led_set.nr_lane = nr_lane; + req->led_set.value = value; +} + +static inline void build_set_lane_fun(struct mbx_fw_cmd_req *req, int nr_lane, + int fun, int value0, int value1, + int value2, int value3) +{ + req->flags = 0; + req->opcode = SET_LANE_FUN; + req->datalen = sizeof(req->set_lane_fun); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->set_lane_fun.func = fun; + req->set_lane_fun.nr_lane = nr_lane; + req->set_lane_fun.value0 = value0; + req->set_lane_fun.value1 = value1; + req->set_lane_fun.value2 = value2; + req->set_lane_fun.value3 = value3; +} + +static inline void build_phy_link_set(struct mbx_fw_cmd_req *req, + unsigned int speeds, int nr_lane) +{ + req->flags = 0; + req->opcode = PHY_LINK_SET; + req->datalen = sizeof(req->phy_link_set); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->phy_link_set.nr_lane = nr_lane; + req->phy_link_set.adv_speed_mask = speeds; +} + +static inline void build_ifup_down(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int up) +{ + req->flags = 0; + req->opcode = IFUP_DOWN; + req->datalen = sizeof(req->ifup); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifup.lane = nr_lane; + req->ifup.up = up; +} + +static inline void build_ifinsmod(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int status) +{ + req->flags = 0; + req->opcode = DRIVER_INSMOD; + req->datalen = sizeof(req->ifinsmod); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifinsmod.lane = nr_lane; + req->ifinsmod.status = status; +} + +static inline void build_ifsuspuse(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int status) +{ + req->flags = 0; + req->opcode = SYSTEM_SUSPUSE; + req->datalen = sizeof(req->ifsuspuse); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifinsmod.lane = nr_lane; + req->ifinsmod.status = status; +} + +static inline void build_mbx_sfp_read(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int sfp_addr, + int reg, int cnt, void *cookie) +{ + req->flags = 0; + req->opcode = SFP_MODULE_READ; + req->datalen = sizeof(req->sfp_read); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->sfp_read.nr_lane = nr_lane; + req->sfp_read.sfp_adr = sfp_addr; + req->sfp_read.reg = reg; + ; + req->sfp_read.cnt = cnt; +} + +static inline void build_mbx_sfp_write(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, int sfp_addr, + int reg, int v) +{ + req->flags = 0; + req->opcode = SFP_MODULE_WRITE; + req->datalen = sizeof(req->sfp_write); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->sfp_write.nr_lane = nr_lane; + req->sfp_write.sfp_adr = sfp_addr; + req->sfp_write.reg = reg; + req->sfp_write.val = v; +} + +/* enum link_event_mask or */ +static inline void build_link_set_event_mask(struct mbx_fw_cmd_req *req, + unsigned short event_mask, + unsigned short enable, + void *cookie) +{ + req->flags = 0; + req->opcode = SET_PF_EVENT_MASK; + req->datalen = sizeof(req->stat_pf_event_mask); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->stat_pf_event_mask.event_mask = event_mask; + req->stat_pf_event_mask.enable_stat = enable; +} + +static inline void build_pluginout_evt_notify(struct mbx_fw_cmd_req *req, + int nr_lane, int dir_plugin, + int enable, void *cookie) +{ + req->flags = 0; + req->opcode = SET_LANE_EVENT_EN; + req->datalen = sizeof(req->stat_lane_evt); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->stat_lane_evt.nr_lane = nr_lane; + if (dir_plugin) { + req->stat_lane_evt.evt.plugin = 1; + req->stat_lane_evt.value.plugin_en = !!enable; + } else { + req->stat_lane_evt.value.plugout_en = !!enable; + req->stat_lane_evt.evt.plugout = 1; + } +} + +static inline void build_lldp_ctrl_set(struct mbx_fw_cmd_req *req, int nr_lane, + int enable) +{ + req->flags = 0; + req->opcode = LLDP_TX_CTL; + req->datalen = sizeof(req->lldp_tx); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + + req->lldp_tx.op = LLDP_TX_SET; + req->lldp_tx.nr_lane = nr_lane; + req->lldp_tx.enable = enable; +} + +static inline void build_lldp_ctrl_get(struct mbx_fw_cmd_req *req, int nr_lane, + void *cookie) +{ + req->flags = 0; + req->opcode = LLDP_TX_CTL; + req->datalen = sizeof(req->lldp_tx); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + + req->lldp_tx.op = LLDP_TX_GET; + req->lldp_tx.nr_lane = nr_lane; +} + +static inline void build_lane_link_change_notify(struct mbx_fw_cmd_req *req, + int nr_lane, int enable, + void *cookie) +{ + req->flags = 0; + req->opcode = SET_LANE_EVENT_EN; + req->datalen = sizeof(req->stat_lane_evt); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->stat_lane_evt.nr_lane = nr_lane; + req->stat_lane_evt.evt.link_change = 1; + req->stat_lane_evt.value.link_change_en = !!enable; +} + +static inline void +build_link_set_loopback_req(struct mbx_fw_cmd_req *req, void *cookie, + enum LOOPBACK_LEVEL level, + enum LOOPBACK_FORCE_SPEED force_speed) +{ + req->flags = 0; + req->opcode = SET_LOOPBACK_MODE; + req->datalen = sizeof(req->loopback); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + + req->loopback.loopback_level = level; + req->loopback.loopback_type = LOOPBACK_TYPE_LOCAL; + if (force_speed != LOOPBACK_FORCE_SPEED_NONE) { + req->loopback.loopback_force_speed = force_speed; + req->loopback.loopback_force_speed_enable = 1; + } +} + +static inline void build_set_phy_reg(struct mbx_fw_cmd_req *req, void *cookie, + enum PHY_INTERFACE phy_inf, char nr_lane, + int reg, int w_data, int recall_qsfp_page) +{ + req->flags = 0; + req->opcode = SET_PHY_REG; + req->datalen = sizeof(req->set_phy_reg); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + + req->set_phy_reg.phy_interface = phy_inf; + req->set_phy_reg.nr_lane = nr_lane; + req->set_phy_reg.phy_reg_addr = reg; + req->set_phy_reg.phy_w_data = w_data; + + if (recall_qsfp_page) + req->set_phy_reg.recall_qsfp_page = 1; + else + req->set_phy_reg.recall_qsfp_page = 0; +} + +static inline void build_get_phy_reg(struct mbx_fw_cmd_req *req, void *cookie, + enum PHY_INTERFACE phy_inf, char nr_lane, + int reg) +{ + req->flags = 0; + req->opcode = GET_PHY_REG; + req->datalen = sizeof(req->get_phy_reg); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + + req->get_phy_reg.phy_interface = phy_inf; + req->get_phy_reg.nr_lane = nr_lane; + req->get_phy_reg.phy_reg_addr = reg; +} + +static inline void build_get_phy_statistics_req(struct mbx_fw_cmd_req *req, + char nr_lane, void *cookie) +{ + req->flags = 0; + req->opcode = GET_PHY_STATISTICS; + req->datalen = sizeof(req->phy_statistics); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->phy_statistics.nr_lane = nr_lane; +} + +static inline void build_comm_sdram_req(struct mbx_fw_cmd_req *req, + void *cookie, u32 op, u32 offset, + u32 fw_bin_phy_lo, u32 fw_bin_phy_hi, + u32 bytes) +{ + req->flags = 0; + req->opcode = EMI_SYNC; + req->datalen = sizeof(req->emi_sync); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->emi_sync.op = op; + req->emi_sync.bytes = bytes; + req->emi_sync.offset = offset; + req->emi_sync.bin_phy_lo = fw_bin_phy_lo; + req->emi_sync.bin_phy_hi = fw_bin_phy_hi; +} + +int rnpm_mbx_phy_write(struct rnpm_hw *hw, u32 reg, u32 val); +int rnpm_mbx_phy_read(struct rnpm_hw *hw, u32 reg, u32 *val); +int rnpm_mbx_sdram_simm(struct rnpm_hw *hw, u32 flags, u32 offset, + void *dma_buf, dma_addr_t dma_phy, u32 len); + +/* =========== errcode======= */ +enum MBX_ERR { + MBX_OK = 0, + MBX_ERR_NO_PERM, + MBX_ERR_INVAL_OPCODE, + MBX_ERR_INVALID_PARAM, + MBX_ERR_INVALID_ADDR, + MBX_ERR_INVALID_LEN, + MBX_ERR_NODEV, + MBX_ERR_IO, +}; + +#endif diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.c b/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.c new file mode 100644 index 000000000000..fb4ee0076b03 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include + +#include "rnpm_common.h" +#include "rnpm_mbx.h" +#include "rnpm_mbx_fw.h" +#include "rnpm_mpe.h" + +#define MPE_FW_BIN "n10c/n10-mpe.bin" +#define MPE_FW_DATA "n10c/n10-mpe-data.bin" +#define MPE_RPU_BIN "n10c/n10-rpu.bin" + +#define CFG_RPU_OFFSET (0x100000) // 4010_0000 broadcast addr +#define START_MPE_REG (0x00198700) // 4019_8700 start all mpe + +// RV_CORE_STATUS: 4000_6000 +#define RV_CORE0_WORING_REG (0x6000) +#define RPU_ID (0x6060) // read-only rpu id + +// RPU_REG +#define RV_BROADCASE_START_REG (0x106000) // broadcast to 0x400X_6000 +#define RPU_DMA_START_REG (0x110000) +#define RPU_ENDIAN_REG (0x110010) +#define N10_START_REG (0x106000) + +#define CFG_MPE_ICCM(nr) (0x200000 + (nr)*0x80000) +#define CFG_MPE_DCCM(nr) (0x220000 + (nr)*0x80000) + +#define RPU_CM3_BASE (0x40000000) // 0x4000_0000 +#define RPU_SDRAM_BASE (0x60000000) // 0x4000_0000 +#define SDRAM_DEFAULT_VAL (0x88481c00) // 0x4000_0000 + +#define iowrite32_arrary(rpubase, offset, array, size) \ + do { \ + int i; \ + for (i = 0; i < size; i++) { \ + rnpm_wr_reg(((char *)(rpubase)) + (offset) + i * 4, \ + (array)[i]); \ + } \ + } while (0) + +#define MPE_LOOP_INST 0x6f + +static void rnpm_reset_mpe_and_rpu(struct rnpm_hw *hw) +{ + // int i; +#define SYSCTL_CRG_CTRL12 0x30007030 +#define RPU_RESET_BIT 9 + + // reset rpu/mpe/pub + cm3_reg_write32(hw, SYSCTL_CRG_CTRL12, BIT(RPU_RESET_BIT + 16) | 0); + /* memory barrior */ + smp_mb(); + mdelay(150); + + cm3_reg_write32(hw, SYSCTL_CRG_CTRL12, + BIT(RPU_RESET_BIT + 16) | BIT(RPU_RESET_BIT)); + /* memory barrior */ + smp_mb(); + + mdelay(100); +} + +static void rnpm_start_rpu(char *rpu_base, int do_start) +{ + int mpe_start_v = 0xff, rpu_start_v = 0x1; + + if (do_start == 0) { + mpe_start_v = 0; + rpu_start_v = 0; + } + rnpm_wr_reg(rpu_base + START_MPE_REG, mpe_start_v); + + // start all rpu-rv-core + rnpm_wr_reg(rpu_base + RV_BROADCASE_START_REG, rpu_start_v); + // start rpu + rnpm_wr_reg(rpu_base + RPU_DMA_START_REG, rpu_start_v); + + /* memory barrior */ + smp_mb(); +} + +/* @rpu_base: mapped(0x4000_0000) + * @mpe_bin : required + * @mpe_data: optional + * @rpu_bin : optional + */ +static int +rnpm_download_and_start_rpu(struct rnpm_hw *hw, char *rpu_base, + const unsigned int *mpe_bin, const int mpe_bin_sz, + const unsigned int *mpe_data, const int mpe_data_sz, + const unsigned int *rpu_bin, const int rpu_sz) +{ + int nr = 0; + + rnpm_info("MPE: rpu:%d mpe:%d mpe-data:%d. Downloading...\n", rpu_sz, + mpe_bin_sz, mpe_data_sz); + + rnpm_reset_mpe_and_rpu(hw); + + // download rpu firmeware + if (rpu_sz) { + iowrite32_arrary(rpu_base, CFG_RPU_OFFSET + 0x4000, rpu_bin, + rpu_sz / 4); + } + + // download firmware to 4 mpe-core: mpe0,mpe1,mpe2,mpe3 + for (nr = 0; nr < 4; nr++) { + iowrite32_arrary(rpu_base, CFG_MPE_ICCM(nr), mpe_bin, + mpe_bin_sz / 4); + if (mpe_data_sz) + iowrite32_arrary(rpu_base, CFG_MPE_DCCM(nr), mpe_data, + mpe_data_sz / 4); + } + /* memory barrior */ + smp_mb(); + + // Enable MPE + if (mpe_src_port != 0) { + rnpm_info("%s %d\n", __func__, __LINE__); + rnpm_wr_reg(rpu_base + 0x100000, mpe_pkt_version); + rnpm_wr_reg(rpu_base + 0x100004, mpe_src_port); + } + + // start mpe + rnpm_wr_reg(rpu_base + RPU_ENDIAN_REG, 0xf); + /* memory barrior */ + smp_mb(); + rnpm_start_rpu(rpu_base, 1); + + return 0; +} + +/* load fw bin from: /lib/firmware/ directory */ +static const struct firmware *rnpm_load_fw(struct device *dev, + const char *fw_name) +{ + const struct firmware *fw; + int rc; + + rc = request_firmware(&fw, fw_name, dev); + if (rc != 0) { + // dev_warn( dev, "Failed to requesting firmware file: %s, %d\n", + // fw_name, rc); + return NULL; + } + + return fw; +} + +int rnpm_rpu_mpe_start(struct rnpm_pf_adapter *adapter) +{ + const struct firmware *mpe_bin = NULL, *mpe_data = NULL, + *rpu_bin = NULL; + struct rnpm_hw *hw = &adapter->hw; + int rpu_version, err = 0; + // u32 val = 0; + + rpu_version = cm3_reg_read32(hw, RPU_CM3_BASE + RPU_ID); + dev_info(&adapter->pdev->dev, "rpu_version:0x%x\n", rpu_version); + + if (rpu_version != 0x20201125) { + dev_info(&adapter->pdev->dev, "rpu not enabled!\n"); + return -1; + } + + dev_info(&adapter->pdev->dev, "rpu_addr=%p\n", hw->rpu_addr); + if (hw->rpu_addr == NULL) + return -EINVAL; + + mpe_bin = rnpm_load_fw(&adapter->pdev->dev, MPE_FW_BIN); + if (!mpe_bin) { + dev_warn(&adapter->pdev->dev, "can't load mpe fw:%s\n", + MPE_FW_BIN); + goto quit; + } + mpe_data = rnpm_load_fw(&adapter->pdev->dev, MPE_FW_DATA); + if (!mpe_data) + dev_warn(&adapter->pdev->dev, "no %s, ignored\n", MPE_FW_DATA); + rpu_bin = rnpm_load_fw(&adapter->pdev->dev, MPE_RPU_BIN); + if (!rpu_bin) + dev_warn(&adapter->pdev->dev, "no %s, ignored\n", MPE_RPU_BIN); + + err = rnpm_download_and_start_rpu( + hw, hw->rpu_addr, (unsigned int *)mpe_bin->data, mpe_bin->size, + mpe_data ? (unsigned int *)mpe_data->data : NULL, + mpe_data ? mpe_data->size : 0, + rpu_bin ? (unsigned int *)rpu_bin->data : NULL, + rpu_bin ? rpu_bin->size : 0); + if (err != 0) { + dev_warn(&adapter->pdev->dev, "can't start mpe and rpu\n"); + goto quit; + } + + adapter->rpu_inited = 1; + +quit: + if (rpu_bin) + release_firmware(rpu_bin); + if (mpe_data) + release_firmware(mpe_data); + if (mpe_bin) + release_firmware(mpe_bin); + return 0; +} + +void rnpm_rpu_mpe_stop(struct rnpm_pf_adapter *adapter) +{ + if (adapter->rpu_inited) { + rnpm_start_rpu(adapter->hw.rpu_addr, 0); + rnpm_reset_mpe_and_rpu(&adapter->hw); + } + + adapter->rpu_inited = 0; +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.h b/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.h new file mode 100644 index 000000000000..f95d3379b212 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_mpe.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef RNMP_MPE_H +#define RNMP_MPE_H + +#include "rnpm.h" + +extern unsigned int mpe_src_port; +extern unsigned int mpe_pkt_version; +int rnpm_rpu_mpe_start(struct rnpm_pf_adapter *adapter); +void rnpm_rpu_mpe_stop(struct rnpm_pf_adapter *adapter); + +#endif // RNMP_MPE diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_n10.c b/drivers/net/ethernet/mucse/rnpm/rnpm_n10.c new file mode 100644 index 000000000000..8d023ad2a779 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_n10.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include + +#include "rnpm.h" +#include "rnpm_phy.h" +#include "rnpm_mbx.h" +#include "rnpm_pcs.h" +#include "rnpm_mbx_fw.h" + +#define RNPM_N10_MAX_TX_QUEUES 128 +#define RNPM_N10_MAX_RX_QUEUES 128 + +#define RNPM_N400_MAX_TX_QUEUES 16 +#define RNPM_N400_MAX_RX_QUEUES 16 + +#define RNPM_N10_NCSI_RAR_ENTRIES (hw->ncsi_rar_entries) /*4*/ +#define RNPM_N10_RAR_ENTRIES (128 - RNPM_N10_NCSI_RAR_ENTRIES) +#define RNPM_N10_MC_TBL_SIZE 128 +#define RNPM_N10_MC_TBL_SIZE_MAC 8 +#define RNPM_N10_VFT_TBL_SIZE 128 +#define RNPM_N10_VFT_TBL_SIZE_MAC 1 +#define RNPM_N10_RX_PB_SIZE 512 +#define RNPM_N10_MSIX_VECTORS 64 + +#define NET_FEATURE_TCAM 1 + +static bool rnpm_mng_enabled(struct rnpm_hw *hw) +{ + return false; +} +__maybe_unused static void rnpm_init_mac_link_ops_n10(struct rnpm_hw *hw) +{ +} + +static s32 rnpm_get_invariants_n10(struct rnpm_hw *hw) +{ + struct rnpm_mac_info *mac = &hw->mac; + + // rnpm_init_mac_link_ops_n10(hw); + // mode is setup here + switch (hw->mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + mac->mc_location = rnpm_mc_location_nic; + mac->mcft_size = RNPM_N10_MC_TBL_SIZE; + mac->mc_filter_type = rnpm_mc_filter_type0; + mac->vlan_location = rnpm_vlan_location_nic; + mac->vft_size = RNPM_N10_VFT_TBL_SIZE; + break; + case MODE_NIC_MODE_2PORT: + case MODE_NIC_MODE_4PORT: + mac->mc_filter_type = rnpm_mc_filter_type4; + mac->mc_location = rnpm_mc_location_mac; + mac->mcft_size = RNPM_N10_MC_TBL_SIZE_MAC; + mac->vlan_location = rnpm_vlan_location_mac; + mac->vft_size = RNPM_N10_VFT_TBL_SIZE_MAC; + + break; + } + + hw->usecstocount = hw->axi_mhz; + hw->dma_split_size = RNPM_RXBUFFER_1536; + hw->ncsi_vf_cpu_shm_pf_base = RNPM_VF_CPU_SHM_BASE_NR62; + hw->ncsi_mc_count = RNPM_NCSI_MC_COUNT; + hw->ncsi_vlan_count = RNPM_NCSI_VLAN_COUNT; + mac->num_rar_entries = RNPM_N10_RAR_ENTRIES; + mac->max_rx_queues = RNPM_N10_MAX_RX_QUEUES; + mac->max_tx_queues = RNPM_N10_MAX_TX_QUEUES; + // mac->max_msix_vectors = rnpm_get_pcie_msix_count_generic(hw); + mac->max_msix_vectors = RNPM_N10_MSIX_VECTORS; + hw->wol_supported = WAKE_MAGIC; + hw->feature_flags |= + RNPM_NET_FEATURE_SG | RNPM_NET_FEATURE_TX_CHECKSUM | + RNPM_NET_FEATURE_RX_CHECKSUM | RNPM_NET_FEATURE_TSO | + RNPM_NET_FEATURE_TX_UDP_TUNNEL | RNPM_NET_FEATURE_VLAN_FILTER | + /*RNPM_NET_FEATURE_VLAN_OFFLOAD |*/ RNPM_NET_FEATURE_TCAM | + RNPM_NET_FEATURE_RX_HASH | RNPM_NET_FEATURE_RX_FCS; + if (!hw->ncsi_en) + hw->feature_flags |= RNPM_NET_FEATURE_VLAN_OFFLOAD; + + return 0; +} + +static s32 rnpm_get_invariants_n400(struct rnpm_hw *hw) +{ + struct rnpm_mac_info *mac = &hw->mac; + + // rnpm_init_mac_link_ops_n10(hw); + // mode is setup here + switch (hw->mode) { + case MODE_NIC_MODE_1PORT_40G: + case MODE_NIC_MODE_1PORT: + mac->mc_location = rnpm_mc_location_nic; + mac->mcft_size = RNPM_N10_MC_TBL_SIZE; + mac->mc_filter_type = rnpm_mc_filter_type0; + mac->vlan_location = rnpm_vlan_location_nic; + mac->vft_size = RNPM_N10_VFT_TBL_SIZE; + break; + case MODE_NIC_MODE_2PORT: + case MODE_NIC_MODE_4PORT: + mac->mc_filter_type = rnpm_mc_filter_type4; + mac->mc_location = rnpm_mc_location_mac; + mac->mcft_size = RNPM_N10_MC_TBL_SIZE_MAC; + mac->vlan_location = rnpm_vlan_location_mac; + mac->vft_size = RNPM_N10_VFT_TBL_SIZE_MAC; + + break; + } + + hw->usecstocount = hw->axi_mhz; + hw->dma_split_size = RNPM_RXBUFFER_1536; + hw->ncsi_vf_cpu_shm_pf_base = RNPM_VF_CPU_SHM_BASE_NR62; + hw->ncsi_mc_count = RNPM_NCSI_MC_COUNT; + hw->ncsi_vlan_count = RNPM_NCSI_VLAN_COUNT; + mac->num_rar_entries = RNPM_N10_RAR_ENTRIES; + mac->max_rx_queues = RNPM_N400_MAX_RX_QUEUES; + mac->max_tx_queues = RNPM_N400_MAX_TX_QUEUES; + // mac->max_msix_vectors = rnpm_get_pcie_msix_count_generic(hw); + mac->max_msix_vectors = RNPM_N10_MSIX_VECTORS; + hw->wol_supported = WAKE_MAGIC; + hw->feature_flags |= + RNPM_NET_FEATURE_SG | RNPM_NET_FEATURE_TX_CHECKSUM | + RNPM_NET_FEATURE_RX_CHECKSUM | RNPM_NET_FEATURE_TSO | + RNPM_NET_FEATURE_TX_UDP_TUNNEL | RNPM_NET_FEATURE_VLAN_FILTER | + /*RNPM_NET_FEATURE_VLAN_OFFLOAD |*/ RNPM_NET_FEATURE_TCAM | + RNPM_NET_FEATURE_RX_HASH | RNPM_NET_FEATURE_RX_FCS; + if (!hw->ncsi_en) + hw->feature_flags |= RNPM_NET_FEATURE_VLAN_OFFLOAD; + + return 0; +} + +/** + * rnpm_init_phy_ops_n10 - PHY/SFP specific init + * @hw: pointer to hardware structure + * + * Initialize any function pointers that were not able to be + * set during get_invariants because the PHY/SFP type was + * not known. Perform the SFP init if necessary. + * + **/ +static s32 rnpm_init_phy_ops_n10(struct rnpm_hw *hw) +{ + s32 ret_val = 0; + + hw->phy.sfp_setup_needed = true; + return ret_val; +} + +static s32 rnpm_setup_sfp_modules_n10(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_reinit_fdir_tables_n10 - Reinitialize Flow Director tables. + * @hw: pointer to hardware structure + **/ +s32 rnpm_reinit_fdir_tables_n10(struct rnpm_hw *hw) +{ + return 0; +} + +/** + * rnpm_fdir_enable_n10 - Initialize Flow Director control registers + * @hw: pointer to hardware structure + * @fdirctrl: value to write to flow director control register + **/ +__maybe_unused static void rnpm_fdir_enable_n10(struct rnpm_hw *hw, + u32 fdirctrl) +{ +} + +/** + * rnpm_init_fdir_signature_n10 - Initialize Flow Director signature filters + * @hw: pointer to hardware structure + * @fdirctrl: value to write to flow director control register, initially + * contains just the value of the Rx packet buffer allocation + **/ +s32 rnpm_init_fdir_signature_n10(struct rnpm_hw *hw, u32 fdirctrl) +{ + return 0; +} + +/** + * rnpm_init_fdir_perfect_n10 - Initialize Flow Director perfect filters + * @hw: pointer to hardware structure + * @fdirctrl: value to write to flow director control register, initially + * contains just the value of the Rx packet buffer allocation + **/ +s32 rnpm_init_fdir_perfect_n10(struct rnpm_hw *hw, u32 fdirctrl) +{ + return 0; +} + +/* These defines allow us to quickly generate all of the necessary instructions + * in the function below by simply calling out RNPM_COMPUTE_SIG_HASH_ITERATION + * for values 0 through 15 + */ +#define RNPM_ATR_COMMON_HASH_KEY \ + (RNPM_ATR_BUCKET_HASH_KEY & RNPM_ATR_SIGNATURE_HASH_KEY) +#define RNPM_COMPUTE_SIG_HASH_ITERATION(_n) \ + do { \ + } while (0) + +/** + * rnpm_atr_compute_sig_hash_n10 - Compute the signature hash + * @stream: input bitstream to compute the hash on + * + * This function is almost identical to the function above but contains + * several optomizations such as unwinding all of the loops, letting the + * compiler work out all of the conditional ifs since the keys are static + * defines, and computing two keys at once since the hashed dword stream + * will be the same for both keys. + **/ +__maybe_unused static u32 +rnpm_atr_compute_sig_hash_n10(union rnpm_atr_hash_dword input, + union rnpm_atr_hash_dword common) +{ + return 0; +} + +/** + * rnpm_atr_add_signature_filter_n10 - Adds a signature hash filter + * @hw: pointer to hardware structure + * @input: unique input dword + * @common: compressed common input dword + * @queue: queue index to direct traffic to + **/ +s32 rnpm_fdir_add_signature_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_hash_dword input, + union rnpm_atr_hash_dword common, + u8 queue) +{ + return 0; +} + +#define RNPM_COMPUTE_BKT_HASH_ITERATION(_n) \ + do { \ + u32 n = (_n); \ + if (RNPM_ATR_BUCKET_HASH_KEY & (0x01 << n)) \ + bucket_hash ^= lo_hash_dword >> n; \ + if (RNPM_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \ + bucket_hash ^= hi_hash_dword >> n; \ + } while (0) + +/** + * rnpm_atr_compute_perfect_hash_n10 - Compute the perfect filter hash + * @atr_input: input bitstream to compute the hash on + * @input_mask: mask for the input bitstream + * + * This function serves two main purposes. First it applies the input_mask + * to the atr_input resulting in a cleaned up atr_input data stream. + * Secondly it computes the hash and stores it in the bkt_hash field at + * the end of the input byte stream. This way it will be available for + * future use without needing to recompute the hash. + **/ +void rnpm_atr_compute_perfect_hash_n10(union rnpm_atr_input *input, + union rnpm_atr_input *input_mask) +{ +} + +/** + * rnpm_get_fdirtcpm_n10 - generate a tcp port from atr_input_masks + * @input_mask: mask to be bit swapped + * + * The source and destination port masks for flow director are bit swapped + * in that bit 15 effects bit 0, 14 effects 1, 13, 2 etc. In order to + * generate a correctly swapped value we need to bit swap the mask and that + * is what is accomplished by this function. + **/ +__maybe_unused static u32 +rnpm_get_fdirtcpm_n10(union rnpm_atr_input *input_mask) +{ + return 0; +} + +/* These two macros are meant to address the fact that we have registers + * that are either all or in part big-endian. As a result on big-endian + * systems we will end up byte swapping the value to little-endian before + * it is byte swapped again and written to the hardware in the original + * big-endian format. + */ +#define RNPM_STORE_AS_BE32(_value) \ + (((u32)(_value) >> 24) | (((u32)(_value)&0x00FF0000) >> 8) | \ + (((u32)(_value)&0x0000FF00) << 8) | ((u32)(_value) << 24)) + +#define RNPM_WRITE_REG_BE32(a, reg, value) \ + RNPM_WRITE_REG((a), (reg), RNPM_STORE_AS_BE32(ntohl(value))) + +#define RNPM_STORE_AS_BE16(_value) \ + ntohs(((u16)(_value) >> 8) | ((u16)(_value) << 8)) + +s32 rnpm_fdir_set_input_mask_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input_mask) +{ + return 0; +} + +s32 rnpm_fdir_write_perfect_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 soft_id, + u8 queue) +{ + return 0; +} + +s32 rnpm_fdir_erase_perfect_filter_n10(struct rnpm_hw *hw, + union rnpm_atr_input *input, u16 soft_id) +{ + s32 err = 0; + + return err; +} + +/** + * rnpm_identify_phy_n10 - Get physical layer module + * @hw: pointer to hardware structure + * + * Determines the physical layer module found on the current adapter. + * If PHY already detected, maintains current PHY type in hw struct, + * otherwise executes the PHY detection routine. + **/ +static s32 rnpm_identify_phy_n10(struct rnpm_hw *hw) +{ + // s32 status = RNPM_ERR_PHY_ADDR_INVALID; + + hw->phy.type = rnpm_phy_sfp; + + return 0; +} + +static s32 rnpm_identify_sfp_module_n10(struct rnpm_hw *hw) +{ + hw->phy.sfp_type = rnpm_sfp_type_da_cu; + + return 0; +} + +/** + * rnpm_enable_rx_dma_n10 - Enable the Rx DMA unit on n10 + * @hw: pointer to hardware structure + * @regval: register value to write to RXCTRL + * + * Enables the Rx DMA unit for n10 + **/ +static s32 rnpm_enable_rx_dma_n10(struct rnpm_hw *hw, u32 regval) +{ + /* Workaround for n10 silicon errata when enabling the Rx datapath. + * If traffic is incoming before we enable the Rx unit, it could hang + * the Rx DMA unit. Therefore, make sure the security engine is + * completely disabled prior to enabling the Rx unit. + */ + hw->mac.ops.disable_rx_buff(hw); + + // RNPM_WRITE_REG(hw, RNPM_RXCTRL, regval); + + hw->mac.ops.enable_rx_buff(hw); + + return 0; +} + +/** + * rnpm_verify_fw_version_n10 - verify fw version for n10 + * @hw: pointer to hardware structure + * + * Verifies that installed the firmware version is 0.6 or higher + * for SFI devices. All n10 SFI devices should have version 0.6 or higher. + * + * Returns RNPM_ERR_EEPROM_VERSION if the FW is not present or + * if the FW version is not supported. + **/ +static s32 rnpm_verify_fw_version_n10(struct rnpm_hw *hw) +{ + // s32 status = RNPM_ERR_EEPROM_VERSION; + // u16 fw_offset, fw_ptp_cfg_offset; + // u16 fw_version = 0; + + return 0; +} + +/** + * rnpm_verify_lesm_fw_enabled_n10 - Checks LESM FW module state. + * @hw: pointer to hardware structure + * + * Returns true if the LESM FW module is present and enabled. Otherwise + * returns false. Smart Speed must be disabled if LESM FW module is enabled. + **/ +bool rnpm_verify_lesm_fw_enabled_n10(struct rnpm_hw *hw) +{ + bool lesm_enabled = false; + + return lesm_enabled; +} + +/** + * rnpm_read_eeprom_buffer_n10 - Read EEPROM word(s) using + * fastest available method + * + * @hw: pointer to hardware structure + * @offset: offset of word in EEPROM to read + * @words: number of words + * @data: word(s) read from the EEPROM + * + * Retrieves 16 bit word(s) read from EEPROM + **/ +__maybe_unused static s32 rnpm_read_eeprom_buffer_n10(struct rnpm_hw *hw, + u16 offset, u16 words, + u16 *data) +{ + s32 ret_val = RNPM_ERR_CONFIG; + + return ret_val; +} + +/** + * rnpm_read_eeprom_n10 - Read EEPROM word using + * fastest available method + * + * @hw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM + **/ +__maybe_unused static s32 rnpm_read_eeprom_n10(struct rnpm_hw *hw, u16 offset, + u16 *data) +{ + s32 ret_val = RNPM_ERR_CONFIG; + + return ret_val; +} + +/** + * rnpm_reset_pipeline_n10 - perform pipeline reset + * + * @hw: pointer to hardware structure + * + * Reset pipeline by asserting Restart_AN together with LMS change to ensure + * full pipeline reset. Note - We must hold the SW/FW semaphore before writing + * to AUTOC, so this function assumes the semaphore is held. + **/ +s32 rnpm_reset_pipeline_n10(struct rnpm_hw *hw) +{ + s32 ret_val; + u32 i; + + /* Enable link if disabled in NVM */ + + /* Write AUTOC register with toggled LMS[2] bit and Restart_AN */ + + /* Wait for AN to leave state 0 */ + for (i = 0; i < 10; i++) { + usleep_range(4000, 8000); + break; + } + + ret_val = 0; + + // reset_pipeline_out: + /* Write AUTOC register with original LMS field and Restart_AN */ + + return ret_val; +} + +__maybe_unused static void upl_init(u8 __iomem *bar2) +{ + int data; +#define SOFT_COMMON11 (0x0007000 + 0xf2c) +#define SOFT_COMMON12 (0x0007000 + 0xf30) + + // config ulh pll + data = ioread32((void *)(bar2 + SOFT_COMMON11)); + iowrite32(((0x3 << 29) | data), + (void *)(bar2 + SOFT_COMMON11)); // ulh pd is 1, bypass is 1 + data = ioread32((void *)(bar2 + SOFT_COMMON11)); + iowrite32(((0x1 << 31) | data), + (void *)(bar2 + SOFT_COMMON11)); // ulh reset is 1 + + data = ioread32((void *)(bar2 + SOFT_COMMON12)); + iowrite32(((0x3 << 29) | data), + (void *)(bar2 + SOFT_COMMON12)); // ulh pd is 1, bypass is 1 + data = ioread32((void *)(bar2 + SOFT_COMMON12)); + iowrite32(((0x1 << 31) | data), + (void *)(bar2 + SOFT_COMMON12)); // ulh reset is 1 +} + +/** + * rnpm_reset_hw_n10 - Perform hardware reset + * @hw: pointer to hardware structure + * + * Resets the hardware by resetting the transmit and receive units, masks + * and clears all interrupts, perform a PHY reset, and perform a link (MAC) + * reset. + **/ +static s32 rnpm_reset_hw_n10(struct rnpm_hw *hw) +{ + s32 status = 0; + // struct rnpm_adapter *adapter = (struct rnpm_adapter *)hw->back; + // int port = hw->num; + + /* Identify PHY and related function pointers */ + status = hw->phy.ops.init(hw); + + /* Setup SFP module if there is one present. */ + if (hw->phy.sfp_setup_needed) { + status = hw->mac.ops.setup_sfp(hw); + hw->phy.sfp_setup_needed = false; + } + + /* Reset PHY */ + if (hw->phy.reset_disable == false && hw->phy.ops.reset != NULL) + hw->phy.ops.reset(hw); + + /* Store the permanent mac address only once */ + if (!(hw->mac.mac_flags & RNPM_FLAGS_INIT_MAC_ADDRESS)) { + rnpm_get_permtion_mac_addr(hw, hw->mac.perm_addr); + memcpy(hw->mac.addr, hw->mac.perm_addr, ETH_ALEN); + } + + hw->mac.num_rar_entries = RNPM_N10_RAR_ENTRIES; + hw->mac.ops.init_rx_addrs(hw); + + return 0; +} + +/** + * rnpm_start_hw_n10 - Prepare hardware for Tx/Rx + * @hw: pointer to hardware structure + * + * Starts the hardware using the generic start_hw function + * and the generation start_hw function. + * Then performs revision-specific operations, if any. + **/ +static s32 rnpm_start_hw_n10(struct rnpm_hw *hw) +{ + s32 ret_val = 0; + + ret_val = rnpm_start_hw_generic(hw); + if (ret_val != 0) + goto out; + + ret_val = rnpm_start_hw_gen2(hw); + if (ret_val != 0) + goto out; + + // ETH Registers + // wr32(hw, RNPM_ETH_ERR_MASK_VECTOR, ~ETH_IGNORE_ALL_ERR); + // wr32(hw, RNPM_ETH_ERR_MASK_VECTOR, 0); + wr32(hw, RNPM_ETH_ERR_MASK_VECTOR, + INNER_L4_BIT | PKT_LEN_ERR | HDR_LEN_ERR); + wr32(hw, RNPM_ETH_BYPASS, 0); + wr32(hw, RNPM_ETH_DEFAULT_RX_RING, 0); + // DMA common Registers + wr32(hw, RNPM_DMA_CONFIG, DMA_VEB_BYPASS); + + // enable-dma-axi + wr32(hw, RNPM_DMA_AXI_EN, (RX_AXI_RW_EN | TX_AXI_RW_EN)); + + if (ret_val == 0) + ret_val = rnpm_verify_fw_version_n10(hw); +out: + return ret_val; +} + +/** + * rnpm_get_media_type_n10 - Get media type + * @hw: pointer to hardware structure + * + * Returns the media type (fiber, copper, backplane) + **/ +static enum rnpm_media_type rnpm_get_media_type_n10(struct rnpm_hw *hw) +{ + enum rnpm_media_type media_type = rnpm_media_type_fiber; + return media_type; +} + +/** + * rnpm_get_supported_physical_layer_n10 - Returns physical layer type + * @hw: pointer to hardware structure + * + * Determines physical layer capabilities of the current configuration. + **/ +static u32 rnpm_get_supported_physical_layer_n10(struct rnpm_hw *hw) +{ + u32 physical_layer = 0; + + return physical_layer; +} + +static s32 rnpm_get_link_capabilities_n10(struct rnpm_hw *hw, + rnpm_link_speed *speed, bool *autoneg, + u32 *media_type) +{ + *autoneg = false; + + switch (hw->phy_type) { + case PHY_TYPE_SGMII: + // *media_type = rnpm_media_type_copper; + *autoneg = true; + break; + default: + *media_type = rnpm_media_type_fiber; + *autoneg = false; + + break; + } + + return 0; +} + +static struct rnpm_phy_operations phy_ops_n10 = { + .identify = &rnpm_identify_phy_n10, + .identify_sfp = &rnpm_identify_sfp_module_n10, + .init = &rnpm_init_phy_ops_n10, + .reset = &rnpm_reset_phy_generic, + .read_reg = &rnpm_read_phy_reg_generic, + .write_reg = &rnpm_write_phy_reg_generic, + .setup_link = &rnpm_setup_phy_link_generic, + .setup_link_speed = &rnpm_setup_phy_link_speed_generic, + .read_i2c_byte = &rnpm_read_i2c_byte_generic, + .write_i2c_byte = &rnpm_write_i2c_byte_generic, + .read_i2c_sff8472 = &rnpm_read_i2c_sff8472_generic, + .read_i2c_eeprom = &rnpm_read_i2c_eeprom_generic, + .write_i2c_eeprom = &rnpm_write_i2c_eeprom_generic, + .check_overtemp = &rnpm_tn_check_overtemp, +}; + +static struct rnpm_mac_operations mac_ops_n10 = { + .init_hw = &rnpm_init_hw_generic, + .reset_hw = &rnpm_reset_hw_n10, + .start_hw = &rnpm_start_hw_n10, + .clear_hw_cntrs = &rnpm_clear_hw_cntrs_generic, + .get_media_type = &rnpm_get_media_type_n10, + .get_supported_physical_layer = &rnpm_get_supported_physical_layer_n10, + .enable_rx_dma = &rnpm_enable_rx_dma_n10, + .disable_rx_buff = &rnpm_disable_rx_buff_generic, + .enable_rx_buff = &rnpm_enable_rx_buff_generic, + .get_mac_addr = &rnpm_get_mac_addr_generic, + .get_device_caps = &rnpm_get_device_caps_generic, + .setup_link = &rnpm_setup_phy_link_speed_generic, + .get_wwn_prefix = &rnpm_get_wwn_prefix_generic, + .stop_adapter = &rnpm_stop_adapter_generic, + //.set_rxpba = &rnpm_set_rxpba_generic, + .check_link = &rnpm_check_mac_link_generic, + .get_link_capabilities = &rnpm_get_link_capabilities_n10, + .led_on = &rnpm_led_on_generic, + .led_off = &rnpm_led_off_generic, + .blink_led_start = &rnpm_blink_led_start_generic, + .blink_led_stop = &rnpm_blink_led_stop_generic, + //.get_bus_info = &rnpm_get_bus_info_generic, + .set_rar = &rnpm_set_rar_generic, + .set_rar_mac = &rnpm_set_rar_mac, + .clear_rar = &rnpm_clear_rar_generic, + .clear_rar_mac = &rnpm_clear_rar_mac, + .set_vmdq = &rnpm_set_vmdq_generic, + //.set_vmdq_san_mac = &rnpm_set_vmdq_san_mac_generic, + .clear_vmdq = &rnpm_clear_vmdq_generic, + .init_rx_addrs = &rnpm_init_rx_addrs_generic, + //.update_mc_addr_list = &rnpm_update_mc_addr_list_generic, + .update_mc_addr_list = &rnpm_update_mutiport_mc_addr_list_generic, + .enable_mc = &rnpm_enable_mc_generic, + .disable_mc = &rnpm_disable_mc_generic, + .clear_vfta = &rnpm_clear_vfta_generic, + .set_vfta = &rnpm_set_vfta_generic, + .set_vfta_mac = &rnpm_set_vfta_mac_generic, + .fc_enable = &rnpm_fc_enable_generic, + .setup_fc = &rnpm_setup_fc, + .set_fw_drv_ver = &rnpm_set_fw_drv_ver_generic, + .init_uta_tables = &rnpm_init_uta_tables_generic, + .setup_sfp = &rnpm_setup_sfp_modules_n10, + .get_thermal_sensor_data = &rnpm_get_thermal_sensor_data_generic, + .init_thermal_sensor_thresh = &rnpm_init_thermal_sensor_thresh_generic, + .mng_fw_enabled = &rnpm_mng_enabled, +}; + +//========== n10 =============== +struct rnpm_info rnpm_n10_info = { + .one_pf_with_two_dma = false, + .total_queue_pair_cnts = RNPM_N10_MAX_TX_QUEUES, + .queue_depth = RNPM_DEFAULT_TXD, + .total_msix_table = 64, + .coalesce.tx_work_limit = RNPM_DEFAULT_TX_WORK, + .coalesce.rx_usecs = RNPM_DEFAULT_LOW_RX_USEC, + .coalesce.rx_frames = 1, + //.coalesce.rx_frames = RNPM_RX_PKT_POLL_BUDGET, + .coalesce.tx_usecs = 100, + .coalesce.tx_frames = RNPM_TX_PKT_POLL_BUDGET, + .total_layer2_count = RNPM_MAX_LAYER2_FILTERS, +#if NET_FEATURE_TCAM + .total_tuple5_count = RNPM_MAX_TCAM_FILTERS, +#else + .total_tuple5_count = RNPM_MAX_TUPLE5_FILTERS, +#endif +#ifdef RNPM_FIX_MAC_PADDING + .mac_padding = true, +#endif + .adapter_cnt = 4, + .rss_type = rnpm_rss_n10, + .get_invariants = &rnpm_get_invariants_n10, + .mac_ops = &mac_ops_n10, + .phy_ops = &phy_ops_n10, + .mbx_ops = &mbx_ops_generic, + .pcs_ops = &pcs_ops_generic, +}; + +//========== n10 =============== +struct rnpm_info rnpm_n400_4x1G_info = { + .one_pf_with_two_dma = false, + .total_queue_pair_cnts = RNPM_N400_MAX_TX_QUEUES, + .queue_depth = RNPM_N400_DEFAULT_TXD, + .total_msix_table = 17, + .coalesce.tx_work_limit = RNPM_DEFAULT_TX_WORK, + //.coalesce.rx_usecs = 1200, + .coalesce.rx_usecs = RNPM_DEFAULT_LOW_RX_USEC, + .coalesce.rx_frames = 1, + .coalesce.tx_usecs = 100, + .coalesce.tx_frames = RNPM_TX_PKT_POLL_BUDGET, + .total_layer2_count = RNPM_MAX_LAYER2_FILTERS, +#if NET_FEATURE_TCAM + .total_tuple5_count = RNPM_MAX_TCAM_FILTERS, +#else + .total_tuple5_count = RNPM_MAX_TUPLE5_FILTERS, +#endif +#ifdef RNPM_FIX_MAC_PADDING + .mac_padding = false, +#endif + .adapter_cnt = 2, + .rss_type = rnpm_rss_n10, + .get_invariants = &rnpm_get_invariants_n400, + .mac_ops = &mac_ops_n10, + .phy_ops = &phy_ops_n10, + .mbx_ops = &mbx_ops_generic, + .pcs_ops = &pcs_ops_generic, +}; diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.c b/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.c new file mode 100644 index 000000000000..86bff6bfa846 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include "rnpm_pcs.h" +#include "rnpm_regs.h" +#include "rnpm_common.h" + +static u32 rnpm_read_pcs(struct rnpm_hw *hw, int num, u32 addr) +{ + u32 reg_hi, reg_lo; + u32 value; + + reg_hi = addr >> 8; + reg_lo = (addr & 0xff) << 2; + + wr32(hw, RNPM_PCS_BASE(num) + (0xff << 2), reg_hi); + value = rd32(hw, RNPM_PCS_BASE(num) + reg_lo); + return value; +} + +static void rnpm_write_pcs(struct rnpm_hw *hw, int num, u32 addr, u32 value) +{ + u32 reg_hi, reg_lo; + + reg_hi = addr >> 8; + reg_lo = (addr & 0xff) << 2; + + wr32(hw, RNPM_PCS_BASE(num) + (0xff << 2), reg_hi); + wr32(hw, RNPM_PCS_BASE(num) + reg_lo, value); +} + +struct rnpm_pcs_operations pcs_ops_generic = { + .read = rnpm_read_pcs, + .write = rnpm_write_pcs, +}; diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.h b/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.h new file mode 100644 index 000000000000..aa8de87313ee --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_pcs.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_PCS_H_ +#define _RNPM_PCS_H_ + +extern struct rnpm_pcs_operations pcs_ops_generic; + +#endif diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_phy.c b/drivers/net/ethernet/mucse/rnpm/rnpm_phy.c new file mode 100644 index 000000000000..40548bd08cab --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_phy.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include + +#include "rnpm_common.h" +#include "rnpm_phy.h" +#include "rnpm_mbx_fw.h" + +#define RNPM_PHY_REVISION_MASK 0xFFFFFFF0 +#define RNPM_MAX_PHY_ADDR 32 + +static void rnpm_i2c_start(struct rnpm_hw *hw); +static void rnpm_i2c_stop(struct rnpm_hw *hw); +static s32 rnpm_clock_in_i2c_byte(struct rnpm_hw *hw, u8 *data); +static s32 rnpm_clock_out_i2c_byte(struct rnpm_hw *hw, u8 data); +static s32 rnpm_get_i2c_ack(struct rnpm_hw *hw); +static s32 rnpm_clock_in_i2c_bit(struct rnpm_hw *hw, bool *data); +static s32 rnpm_clock_out_i2c_bit(struct rnpm_hw *hw, bool data); +static void rnpm_raise_i2c_clk(struct rnpm_hw *hw, u32 *i2cctl); +static void rnpm_lower_i2c_clk(struct rnpm_hw *hw, u32 *i2cctl); +static s32 rnpm_set_i2c_data(struct rnpm_hw *hw, u32 *i2cctl, bool data); +static bool rnpm_get_i2c_data(u32 *i2cctl); +static void rnpm_i2c_bus_clear(struct rnpm_hw *hw); +static enum rnpm_phy_type rnpm_get_phy_type_from_id(u32 phy_id); +static s32 rnpm_get_phy_id(struct rnpm_hw *hw); + +/** + * rnpm_identify_phy_generic - Get physical layer module + * @hw: pointer to hardware structure + * + * Determines the physical layer module found on the current adapter. + **/ +s32 rnpm_identify_phy_generic(struct rnpm_hw *hw) +{ + s32 status = RNPM_ERR_PHY_ADDR_INVALID; + + return status; +} + +/** + * rnpm_get_phy_id - Get the phy type + * @hw: pointer to hardware structure + * + **/ +__maybe_unused static s32 rnpm_get_phy_id(struct rnpm_hw *hw) +{ + u32 status; + u16 phy_id_high = 0; + u16 phy_id_low = 0; + + status = hw->phy.ops.read_reg(hw, MDIO_DEVID1, MDIO_MMD_PMAPMD, + &phy_id_high); + + if (status == 0) { + hw->phy.id = (u32)(phy_id_high << 16); + status = hw->phy.ops.read_reg(hw, MDIO_DEVID2, MDIO_MMD_PMAPMD, + &phy_id_low); + hw->phy.id |= (u32)(phy_id_low & RNPM_PHY_REVISION_MASK); + hw->phy.revision = (u32)(phy_id_low & ~RNPM_PHY_REVISION_MASK); + } + return status; +} + +/** + * rnpm_get_phy_type_from_id - Get the phy type + * @hw: pointer to hardware structure + * + **/ +__maybe_unused static enum rnpm_phy_type rnpm_get_phy_type_from_id(u32 phy_id) +{ + enum rnpm_phy_type phy_type = rnpm_phy_unknown; + + return phy_type; +} + +/** + * rnpm_reset_phy_generic - Performs a PHY reset + * @hw: pointer to hardware structure + **/ +s32 rnpm_reset_phy_generic(struct rnpm_hw *hw) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_read_phy_reg_generic - Reads a value from a specified PHY register + * @hw: pointer to hardware structure + * @reg_addr: 32 bit address of PHY register to read + * @phy_data: Pointer to read data from PHY register + **/ +s32 rnpm_read_phy_reg_generic(struct rnpm_hw *hw, u32 reg_addr, u32 device_type, + u16 *phy_data) +{ + s32 status = 0; + u32 data = 0; + + status = rnpm_mbx_phy_read(hw, reg_addr, &data); + *phy_data = data & 0xffff; + + return status; +} + +/** + * rnpm_write_phy_reg_generic - Writes a value to specified PHY register + * @hw: pointer to hardware structure + * @reg_addr: 32 bit PHY register to write + * @device_type: 5 bit device type + * @phy_data: Data to write to the PHY register + **/ +s32 rnpm_write_phy_reg_generic(struct rnpm_hw *hw, u32 reg_addr, + u32 device_type, u16 phy_data) +{ + s32 status = 0; + + status = rnpm_mbx_phy_write(hw, reg_addr, (u32)phy_data); + + return status; +} + +/** + * rnpm_setup_phy_link_generic - Set and restart autoneg + * @hw: pointer to hardware structure + * + * Restart autonegotiation and PHY and waits for completion. + **/ +s32 rnpm_setup_phy_link_generic(struct rnpm_hw *hw) +{ + s32 status = 0; + + rnpm_mbx_phy_link_set(hw, hw->phy.autoneg_advertised); + return status; +} + +/** + * rnpm_setup_phy_link_speed_generic - Sets the auto advertised capabilities + * @hw: pointer to hardware structure + * @speed: new link speed + **/ +s32 rnpm_setup_phy_link_speed_generic(struct rnpm_hw *hw, rnpm_link_speed speed, + bool autoneg_wait_to_complete) +{ + struct rnpm_adapter *adpt = hw->back; + u32 value = 0; + u32 value_r4 = 0; + u32 value_r9 = 0; + + rnpm_logd(LOG_PHY, + "%s setup phy: phy_addr=%d speed=%d duplex=%d autoneg=%d ", + __func__, adpt->phy_addr, speed, hw->mac.duplex, + hw->mac.autoneg); + rnpm_logd(LOG_PHY, "is_backplane=%d is_sgmii=%d mdix=%d\n", + hw->is_backplane, hw->is_sgmii, hw->phy.mdix); + + if (hw->is_backplane) { + /* Backplane type, support AN, unsupport set speed */ + return rnpm_set_lane_fun(hw, LANE_FUN_AN, hw->mac.autoneg, 0, 0, + 0); + } + + /* Fiber only support force speed 1G/10G*/ + if (!hw->is_sgmii) { + if (adpt->pf_adapter->force_10g_1g_speed_ablity) { + rnpm_mbx_force_speed(hw, speed); + /* Update port link info when firber is absent */ + set_bit(RNPM_PF_LINK_CHANGE, &adpt->pf_adapter->flags); + } + return 0; + } + + /* Set MDI/MDIX mode */ + rnpm_mbx_phy_read(hw, RNPM_YT8531_PHY_SPEC_CTRL, &value); + value &= ~RNPM_YT8531_PHY_SPEC_CTRL_MDIX_CFG_MASK; + /* Options: 0: Auto (default) 1: MDI mode 2: MDI-X mode */ + switch (hw->phy.mdix) { + case 1: + break; + case 2: + value |= RNPM_YT8531_PHY_SPEC_CTRL_FORCE_MDIX; + break; + case 0: + default: + value |= RNPM_YT8531_PHY_SPEC_CTRL_AUTO_MDI_MDIX; + break; + } + rnpm_mbx_phy_write(hw, RNPM_YT8531_PHY_SPEC_CTRL, value); + + if (speed == RNPM_LINK_SPEED_UNKNOWN) { + rnpm_mbx_phy_read(hw, 0x0, &value); + value |= RNPM_MDI_PHY_RESET; + rnpm_mbx_phy_write(hw, 0x0, value); + goto skip_an; + } + /* Clear autoneg_advertised and set new values based on input link + * speed. + */ + hw->phy.autoneg_advertised = speed; + + if (!hw->mac.autoneg) { + switch (speed) { + case RNPM_LINK_SPEED_1GB_FULL: + case RNPM_LINK_SPEED_1GB_HALF: + value = RNPM_MDI_PHY_SPEED_SELECT1; + // if (hw->phy.id == RNPM_YT8531_PHY_ID) { + speed = RNPM_LINK_SPEED_1GB_FULL; + goto out; + // } + break; + case RNPM_LINK_SPEED_100_FULL: + case RNPM_LINK_SPEED_100_HALF: + value = RNPM_MDI_PHY_SPEED_SELECT0; + break; + case RNPM_LINK_SPEED_10_FULL: + case RNPM_LINK_SPEED_10_HALF: + value = 0; + break; + default: + value = RNPM_MDI_PHY_SPEED_SELECT0 | + RNPM_MDI_PHY_SPEED_SELECT1; + hw_dbg(hw, "unknown speed = 0x%x.\n", speed); + break; + } + /* duplex full */ + if (hw->mac.duplex) + value |= RNPM_MDI_PHY_DUPLEX; + value |= RNPM_MDI_PHY_RESET; + value &= ~RNPM_MDI_PHY_ANE; + rnpm_mbx_phy_write(hw, 0x0, value); + + goto skip_an; + } + + // start_an: + value_r4 = 0x1E0; + value_r9 = 0x300; + /*disable 100/10base-T Self-negotiation ability*/ + rnpm_mbx_phy_read(hw, 0x4, &value); + value &= ~value_r4; + rnpm_mbx_phy_write(hw, 0x4, value); + + /*disable 1000base-T Self-negotiation ability*/ + rnpm_mbx_phy_read(hw, 0x9, &value); + value &= ~value_r9; + rnpm_mbx_phy_write(hw, 0x9, value); + + value_r4 = 0x0; + value_r9 = 0x0; + + if (speed & RNPM_LINK_SPEED_1GB_FULL) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_1GB_FULL; + value_r9 |= 0x200; + } + if (speed & RNPM_LINK_SPEED_100_FULL) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_100_FULL; + value_r4 |= 0x100; + } + if (speed & RNPM_LINK_SPEED_10_FULL) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_10_FULL; + value_r4 |= 0x40; + } + + if (speed & RNPM_LINK_SPEED_1GB_HALF) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_1GB_HALF; + value_r9 |= 0x100; + } + if (speed & RNPM_LINK_SPEED_100_HALF) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_100_HALF; + value_r4 |= 0x80; + } + if (speed & RNPM_LINK_SPEED_10_HALF) { + hw->phy.autoneg_advertised |= RNPM_LINK_SPEED_10_HALF; + value_r4 |= 0x20; + } + + /* enable 1000base-T Self-negotiation ability */ + rnpm_mbx_phy_read(hw, 0x9, &value); + value |= value_r9; + rnpm_mbx_phy_write(hw, 0x9, value); + + /* enable 100/10base-T Self-negotiation ability */ + rnpm_mbx_phy_read(hw, 0x4, &value); + value |= value_r4; + rnpm_mbx_phy_write(hw, 0x4, value); + + /* software reset to make the above configuration take effect*/ + rnpm_mbx_phy_read(hw, 0x0, &value); + value |= 0x9200; + rnpm_mbx_phy_write(hw, 0x0, value); +skip_an: + /* power on in UTP mode */ + rnpm_mbx_phy_read(hw, 0x0, &value); + value &= ~0x800; + rnpm_mbx_phy_write(hw, 0x0, value); + +out: + return 0; +} + +/** + * rnpm_get_copper_link_capabilities_generic - Determines link capabilities + * @hw: pointer to hardware structure + * @speed: pointer to link speed + * @autoneg: boolean auto-negotiation value + * + * Determines the link capabilities by reading the AUTOC register. + */ +s32 rnpm_get_copper_link_capabilities_generic(struct rnpm_hw *hw, + rnpm_link_speed *speed, + bool *autoneg) +{ + s32 status = RNPM_ERR_LINK_SETUP; + u16 speed_ability; + + *speed = 0; + *autoneg = true; + + status = hw->phy.ops.read_reg(hw, MDIO_SPEED, MDIO_MMD_PMAPMD, + &speed_ability); + + if (status == 0) { + if (speed_ability & MDIO_SPEED_10G) + *speed |= RNPM_LINK_SPEED_10GB_FULL; + if (speed_ability & MDIO_PMA_SPEED_1000) + *speed |= RNPM_LINK_SPEED_1GB_FULL; + if (speed_ability & MDIO_PMA_SPEED_100) + *speed |= RNPM_LINK_SPEED_100_FULL; + } + + return status; +} + +/** + * rnpm_check_phy_link_tnx - Determine link and speed status + * @hw: pointer to hardware structure + * + * Reads the VS1 register to determine if link is up and the current speed for + * the PHY. + **/ +s32 rnpm_check_phy_link_tnx(struct rnpm_hw *hw, rnpm_link_speed *speed, + bool *link_up) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_setup_phy_link_tnx - Set and restart autoneg + * @hw: pointer to hardware structure + * + * Restart autonegotiation and PHY and waits for completion. + **/ +s32 rnpm_setup_phy_link_tnx(struct rnpm_hw *hw) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_get_phy_firmware_version_tnx - Gets the PHY Firmware Version + * @hw: pointer to hardware structure + * @firmware_version: pointer to the PHY Firmware Version + **/ +s32 rnpm_get_phy_firmware_version_tnx(struct rnpm_hw *hw, u16 *firmware_version) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_get_phy_firmware_version_generic - Gets the PHY Firmware Version + * @hw: pointer to hardware structure + * @firmware_version: pointer to the PHY Firmware Version + **/ +s32 rnpm_get_phy_firmware_version_generic(struct rnpm_hw *hw, + u16 *firmware_version) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_reset_phy_nl - Performs a PHY reset + * @hw: pointer to hardware structure + **/ +s32 rnpm_reset_phy_nl(struct rnpm_hw *hw) +{ + s32 ret_val = 0; + + return ret_val; +} + +/** + * rnpm_identify_sfp_module_generic - Identifies SFP modules + * @hw: pointer to hardware structure + * + * Searches for and identifies the SFP module and assigns appropriate PHY type. + **/ +s32 rnpm_identify_sfp_module_generic(struct rnpm_hw *hw) +{ + return RNPM_ERR_SFP_NOT_PRESENT; +} + +/** + * rnpm_get_sfp_init_sequence_offsets - Provides offset of PHY init sequence + * @hw: pointer to hardware structure + * @list_offset: offset to the SFP ID list + * @data_offset: offset to the SFP data block + * + * Checks the MAC's EEPROM to see if it supports a given SFP+ module type, if + * so it returns the offsets to the phy init sequence block. + **/ +s32 rnpm_get_sfp_init_sequence_offsets(struct rnpm_hw *hw, u16 *list_offset, + u16 *data_offset) +{ + return 0; +} + +/** + * rnpm_read_i2c_eeprom_generic - Reads 8 bit EEPROM word over I2C interface + * @hw: pointer to hardware structure + * @byte_offset: EEPROM byte offset to read at address 0xA0 + * @eeprom_data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface. + **/ +s32 rnpm_read_i2c_eeprom_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 *eeprom_data) +{ + return -EIO; +} + +/** + * rnpm_read_i2c_sff8472_generic - Reads 8 bit word over I2C interface + * @hw: pointer to hardware structure + * @byte_offset: byte offset at address 0xA2 + * @eeprom_data: value read + * + * Performs byte read operation to SFP module's SFF-8472 data over I2C + **/ +s32 rnpm_read_i2c_sff8472_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 *sff8472_data) +{ + // *sff8472_data = rnpm_mbx_sfp_read(hw, RNPM_I2C_EEPROM_DEV_ADDR2, + // byte_offset); return hw->phy.ops.read_i2c_byte(hw, byte_offset, + // RNPM_I2C_EEPROM_DEV_ADDR2, sff8472_data); + return -EIO; +} + +/** + * rnpm_write_i2c_eeprom_generic - Writes 8 bit EEPROM word over I2C interface + * @hw: pointer to hardware structure + * @byte_offset: EEPROM byte offset to write + * @eeprom_data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface. + **/ +s32 rnpm_write_i2c_eeprom_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 eeprom_data) +{ + // return hw->phy.ops.write_i2c_byte(hw, byte_offset, + // RNPM_I2C_EEPROM_DEV_ADDR, eeprom_data); + return -EIO; +} + +/** + * rnpm_read_i2c_byte_generic - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface at + * a specified device address. + **/ +s32 rnpm_read_i2c_byte_generic(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr, + u8 *data) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_write_i2c_byte_generic - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface at + * a specified device address. + **/ +s32 rnpm_write_i2c_byte_generic(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr, + u8 data) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_i2c_start - Sets I2C start condition + * @hw: pointer to hardware structure + * + * Sets I2C start condition (High -> Low on SDA while SCL is High) + **/ +__maybe_unused static void rnpm_i2c_start(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_i2c_stop - Sets I2C stop condition + * @hw: pointer to hardware structure + * + * Sets I2C stop condition (Low -> High on SDA while SCL is High) + **/ +__maybe_unused static void rnpm_i2c_stop(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_clock_in_i2c_byte - Clocks in one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte to clock in + * + * Clocks in one byte data via I2C data/clock + **/ +__maybe_unused static s32 rnpm_clock_in_i2c_byte(struct rnpm_hw *hw, u8 *data) +{ + s32 i; + bool bit = false; + + for (i = 7; i >= 0; i--) { + rnpm_clock_in_i2c_bit(hw, &bit); + *data |= bit << i; + } + + return 0; +} + +/** + * rnpm_clock_out_i2c_byte - Clocks out one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte clocked out + * + * Clocks out one byte data via I2C data/clock + **/ +__maybe_unused static s32 rnpm_clock_out_i2c_byte(struct rnpm_hw *hw, u8 data) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_get_i2c_ack - Polls for I2C ACK + * @hw: pointer to hardware structure + * + * Clocks in/out one bit via I2C data/clock + **/ +__maybe_unused static s32 rnpm_get_i2c_ack(struct rnpm_hw *hw) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_clock_in_i2c_bit - Clocks in one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: read data value + * + * Clocks in one bit via I2C data/clock + **/ +__maybe_unused static s32 rnpm_clock_in_i2c_bit(struct rnpm_hw *hw, bool *data) +{ + return 0; +} + +/** + * rnpm_clock_out_i2c_bit - Clocks in/out one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: data value to write + * + * Clocks out one bit via I2C data/clock + **/ +__maybe_unused static s32 rnpm_clock_out_i2c_bit(struct rnpm_hw *hw, bool data) +{ + s32 status = 0; + + return status; +} +/** + * rnpm_raise_i2c_clk - Raises the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Raises the I2C clock line '0'->'1' + **/ +__maybe_unused static void rnpm_raise_i2c_clk(struct rnpm_hw *hw, u32 *i2cctl) +{ +} + +/** + * rnpm_lower_i2c_clk - Lowers the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Lowers the I2C clock line '1'->'0' + **/ +__maybe_unused static void rnpm_lower_i2c_clk(struct rnpm_hw *hw, u32 *i2cctl) +{ +} + +/** + * rnpm_set_i2c_data - Sets the I2C data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * @data: I2C data value (0 or 1) to set + * + * Sets the I2C data bit + **/ +__maybe_unused static s32 rnpm_set_i2c_data(struct rnpm_hw *hw, u32 *i2cctl, + bool data) +{ + s32 status = 0; + + return status; +} + +/** + * rnpm_get_i2c_data - Reads the I2C SDA data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Returns the I2C data bit value + **/ +__maybe_unused static bool rnpm_get_i2c_data(u32 *i2cctl) +{ + bool data = false; + + return data; +} + +/** + * rnpm_i2c_bus_clear - Clears the I2C bus + * @hw: pointer to hardware structure + * + * Clears the I2C bus by sending nine clock pulses. + * Used when data line is stuck low. + **/ +__maybe_unused static void rnpm_i2c_bus_clear(struct rnpm_hw *hw) +{ +} + +/** + * rnpm_tn_check_overtemp - Checks if an overtemp occurred. + * @hw: pointer to hardware structure + * + * Checks if the LASI temp alarm status was triggered due to overtemp + **/ +s32 rnpm_tn_check_overtemp(struct rnpm_hw *hw) +{ + s32 status = 0; + + return status; +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_phy.h b/drivers/net/ethernet/mucse/rnpm/rnpm_phy.h new file mode 100644 index 000000000000..d3af43713a9f --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_phy.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_PHY_H_ +#define _RNPM_PHY_H_ + +#include "rnpm_type.h" +#define RNPM_I2C_EEPROM_DEV_ADDR 0xA0 +#define RNPM_I2C_EEPROM_DEV_ADDR2 0xA2 + +/* PHY YT8531S*/ +#define RNPM_YT8531_PHY_ID 0x4f51e91a +#define RNPM_YT8531_PHY_SPEC_CTRL 0x10 +#define RNPM_YT8531_PHY_SPEC_CTRL_FORCE_MDIX 0x0020 +#define RNPM_YT8531_PHY_SPEC_CTRL_AUTO_MDI_MDIX 0x0060 +#define RNPM_YT8531_PHY_SPEC_CTRL_MDIX_CFG_MASK 0x0060 + +/* EEPROM byte offsets */ +#define SFF_MODULE_ID_OFFSET 0x00 +#define SFF_DIAG_SUPPORT_OFFSET 0x5c +#define SFF_MODULE_ID_SFF 0x2 +#define SFF_MODULE_ID_SFP 0x3 +#define SFF_MODULE_ID_QSFP 0xc +#define SFF_MODULE_ID_QSFP_PLUS 0xd +#define SFF_MODULE_ID_QSFP28 0x11 + +/* Bitmasks */ +#define RNPM_SFF_DA_PASSIVE_CABLE 0x4 +#define RNPM_SFF_DA_ACTIVE_CABLE 0x8 +#define RNPM_SFF_DA_SPEC_ACTIVE_LIMITING 0x4 +#define RNPM_SFF_1GBASESX_CAPABLE 0x1 +#define RNPM_SFF_1GBASELX_CAPABLE 0x2 +#define RNPM_SFF_1GBASET_CAPABLE 0x8 +#define RNPM_SFF_10GBASESR_CAPABLE 0x10 +#define RNPM_SFF_10GBASELR_CAPABLE 0x20 +#define RNPM_SFF_ADDRESSING_MODE 0x4 +#define RNPM_I2C_EEPROM_READ_MASK 0x100 +#define RNPM_I2C_EEPROM_STATUS_MASK 0x3 +#define RNPM_I2C_EEPROM_STATUS_NO_OPERATION 0x0 +#define RNPM_I2C_EEPROM_STATUS_PASS 0x1 +#define RNPM_I2C_EEPROM_STATUS_FAIL 0x2 +#define RNPM_I2C_EEPROM_STATUS_IN_PROGRESS 0x3 + +/* Flow control defines */ +#define RNPM_TAF_SYM_PAUSE 0x400 +#define RNPM_TAF_ASM_PAUSE 0x800 + +/* Bit-shift macros */ +#define RNPM_SFF_VENDOR_OUI_BYTE0_SHIFT 24 +#define RNPM_SFF_VENDOR_OUI_BYTE1_SHIFT 16 +#define RNPM_SFF_VENDOR_OUI_BYTE2_SHIFT 8 + +/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */ +#define RNPM_SFF_VENDOR_OUI_TYCO 0x00407600 +#define RNPM_SFF_VENDOR_OUI_FTL 0x00906500 +#define RNPM_SFF_VENDOR_OUI_AVAGO 0x00176A00 +#define RNPM_SFF_VENDOR_OUI_INTEL 0x001B2100 + +/* I2C SDA and SCL timing parameters for standard mode */ +#define RNPM_I2C_T_HD_STA 4 +#define RNPM_I2C_T_LOW 5 +#define RNPM_I2C_T_HIGH 4 +#define RNPM_I2C_T_SU_STA 5 +#define RNPM_I2C_T_HD_DATA 5 +#define RNPM_I2C_T_SU_DATA 1 +#define RNPM_I2C_T_RISE 1 +#define RNPM_I2C_T_FALL 1 +#define RNPM_I2C_T_SU_STO 4 +#define RNPM_I2C_T_BUF 5 + +#define RNPM_TN_LASI_STATUS_REG 0x9005 +#define RNPM_TN_LASI_STATUS_TEMP_ALARM 0x0008 + +/* SFP+ SFF-8472 Compliance code */ +#define RNPM_SFF_SFF_8472_UNSUP 0x00 + +s32 rnpm_init_phy_ops_generic(struct rnpm_hw *hw); +s32 rnpm_identify_phy_generic(struct rnpm_hw *hw); +s32 rnpm_reset_phy_generic(struct rnpm_hw *hw); +s32 rnpm_read_phy_reg_generic(struct rnpm_hw *hw, u32 reg_addr, u32 device_type, + u16 *phy_data); +s32 rnpm_write_phy_reg_generic(struct rnpm_hw *hw, u32 reg_addr, + u32 device_type, u16 phy_data); +s32 rnpm_setup_phy_link_generic(struct rnpm_hw *hw); +s32 rnpm_setup_phy_link_speed_generic(struct rnpm_hw *hw, rnpm_link_speed speed, + bool autoneg_wait_to_complete); +s32 rnpm_get_copper_link_capabilities_generic(struct rnpm_hw *hw, + rnpm_link_speed *speed, + bool *autoneg); + +/* PHY specific */ +s32 rnpm_check_phy_link_tnx(struct rnpm_hw *hw, rnpm_link_speed *speed, + bool *link_up); +s32 rnpm_setup_phy_link_tnx(struct rnpm_hw *hw); +s32 rnpm_get_phy_firmware_version_tnx(struct rnpm_hw *hw, + u16 *firmware_version); +s32 rnpm_get_phy_firmware_version_generic(struct rnpm_hw *hw, + u16 *firmware_version); + +s32 rnpm_reset_phy_nl(struct rnpm_hw *hw); +s32 rnpm_identify_sfp_module_generic(struct rnpm_hw *hw); +s32 rnpm_get_sfp_init_sequence_offsets(struct rnpm_hw *hw, u16 *list_offset, + u16 *data_offset); +s32 rnpm_tn_check_overtemp(struct rnpm_hw *hw); +s32 rnpm_read_i2c_byte_generic(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr, + u8 *data); +s32 rnpm_write_i2c_byte_generic(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr, + u8 data); +s32 rnpm_read_i2c_eeprom_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 *eeprom_data); +s32 rnpm_read_i2c_sff8472_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 *sff8472_data); +s32 rnpm_write_i2c_eeprom_generic(struct rnpm_hw *hw, u8 byte_offset, + u8 eeprom_data); +#endif /* _RNPM_PHY_H_ */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.c b/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.c new file mode 100644 index 000000000000..8f4f996ef53c --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.c @@ -0,0 +1,804 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +//#include +#include +#include + +#include "rnpm.h" +#include "rnpm_regs.h" +#include "rnpm_ptp.h" + +/* PTP and HW Timer ops */ +static void config_hw_tstamping(void __iomem *ioaddr, u8 port, u32 data) +{ + rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), data); +} + +static void config_sub_second_increment(void __iomem *ioaddr, u8 port, + u32 ptp_clock, u32 *ssinc) +{ + u32 value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + unsigned long data; + u32 reg_value; + + /* in "fine adjustement mode" set sub-second + * increment to twice the number of nanoseconds of a clock cycle. + * The calculation of the default_addend value by the caller will set it + * to mid-range = 2^31 when the remainder of this division is zero, + * which will make the accumulator overflow once every 2 ptp_clock + * cycles, adding twice the number of nanoseconds of a clock cycle : + * 2000000000ULL / ptp_clock. + */ + if (ptp_clock == 0) { + ptp_dbg("%s:%dthis is a bug that the syskernel clock is zero\n", + __func__, __LINE__); + return; + } + if (value & RNPM_PTP_TCR_TSCFUPDT) + data = (2000000000ULL / ptp_clock); + else + data = (1000000000ULL / ptp_clock); + + /* 0.465ns accuracy */ + if (!(value & RNPM_PTP_TCR_TSCTRLSSR)) + data = (data * 1000) / 465; + + data &= RNPM_PTP_SSIR_SSINC_MASK; + + reg_value = data; + + reg_value <<= RNPM_PTP_SSIR_SSINC_SHIFT; + + rnpm_wr_reg(ioaddr + RNPM_MAC_SUB_SECOND_INCREMENT(port), reg_value); + + if (ssinc) + *ssinc = data; +} + +static int config_addend(void __iomem *ioaddr, u8 port, u32 addend) +{ + u32 value; + int limit; + + rnpm_wr_reg(ioaddr + RNPM_MAC_TS_ADDEND(port), addend); + /* issue command to update the addend value */ + value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + value |= RNPM_PTP_TCR_TSADDREG; + rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value); + + /* wait for present addend update to complete */ + limit = 10; + while (limit--) { + if (!(rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)) & + RNPM_PTP_TCR_TSADDREG)) + break; + mdelay(10); + } + if (limit < 0) + return -EBUSY; + + return 0; +} + +static int init_systime(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec) +{ + u32 value; + u32 target; + int timeout = 0; + + //printk("init systime: %x\n", sec); + rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_UPDATE(port), sec); + rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_UPDATE(port), nsec); + /* issue command to initialize the system time value */ + value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + value |= RNPM_PTP_TCR_TSINIT; + rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value); + + while (timeout <= 20) { + target = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + if (!(value & RNPM_PTP_TCR_TSINIT)) + break; + usleep_range(5000, 10000); + timeout++; + } + + if (timeout < 20) + return 0; + else + return -1; + + /* wait for present system time initialize to complete */ + //return readl_poll_timeout(ioaddr + RNPM_MAC_TS_CTRL(port), value, + // !(value & RNPM_PTP_TCR_TSINIT), + // 10000, 100000); +} + +static void get_systime(void __iomem *ioaddr, u8 port, u64 *systime) +{ + u64 ns; + + /* Get the TSSS value */ + ns = rnpm_rd_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_CFG(port)); + /* Get the TSS and convert sec time value to nanosecond */ + ns += rnpm_rd_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_CFG(port)) * + 1000000000ULL; + + if (systime) + *systime = ns; +} + +static void config_mac_interrupt_enable(void __iomem *ioaddr, u8 port, bool on) +{ + rnpm_wr_reg(ioaddr + RNPM_MAC_INTERRUPT_ENABLE(port), on); +} + +struct rnpm_hwtimestamp { + void (*config_hw_tstamping)(void __iomem *ioaddr, u8 port, u32 data); + void (*config_sub_second_increment)(void __iomem *ioaddr, u8 port, + u32 ptp_clock, u32 *ssinc); + void (*config_mac_irq_enable)(void __iomem *ioaddr, u8 port, bool on); + int (*init_systime)(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec); + int (*config_addend)(void __iomem *ioaddr, u8 port, u32 addend); + int (*adjust_systime)(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec, + int add_sub); + void (*get_systime)(void __iomem *ioaddr, u8 port, u64 *systime); +}; + +static int adjust_systime(void __iomem *ioaddr, u8 port, u32 sec, u32 nsec, + int add_sub) +{ + u32 value; + int limit; + + if (add_sub) { + /* If the new sec value needs to be subtracted with + * the system time, then RNPM_MAC_STSUR reg should be + * programmed with (2^32 – ) + */ + sec = -sec; + + value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + if (value & RNPM_PTP_TCR_TSCTRLSSR) + nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec); + else + nsec = (PTP_BINARY_ROLLOVER_MODE - nsec); + } + + //printk("adjust %x\n", sec); + rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_SEC_UPDATE(port), sec); + value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec; + rnpm_wr_reg(ioaddr + RNPM_MAC_SYS_TIME_NANOSEC_UPDATE(port), value); + + /* issue command to initialize the system time value */ + value = rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)); + value |= RNPM_PTP_TCR_TSUPDT; + rnpm_wr_reg(ioaddr + RNPM_MAC_TS_CTRL(port), value); + + /* wait for present system time adjust/update to complete */ + limit = 10; + while (limit--) { + if (!(rnpm_rd_reg(ioaddr + RNPM_MAC_TS_CTRL(port)) & + RNPM_PTP_TCR_TSUPDT)) + break; + mdelay(10); + } + if (limit < 0) + return -EBUSY; + return 0; +} + +const struct rnpm_hwtimestamp mac_ptp = { + .config_hw_tstamping = config_hw_tstamping, + .config_mac_irq_enable = config_mac_interrupt_enable, + .init_systime = init_systime, + .config_sub_second_increment = config_sub_second_increment, + .config_addend = config_addend, + .adjust_systime = adjust_systime, + .get_systime = get_systime, +}; + +static int rnpm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct rnpm_adapter *pf = + container_of(ptp, struct rnpm_adapter, ptp_clock_ops); + unsigned long flags; + u32 diff, addend; + int neg_adj = 0; + u64 adj; + u8 port = pf->port; + + if (pf == NULL) { + ptp_dbg("adapter_of contail is null\n"); + return 0; + } + + if (scaled_ppm < 0) { + neg_adj = 1; + scaled_ppm = -scaled_ppm; + } + addend = pf->default_addend; + adj = addend; + + adj *= (u64)scaled_ppm; + diff = div64_u64(adj, 1000000ULL << 16); + addend = neg_adj ? (addend - diff) : (addend + diff); + spin_lock_irqsave(&pf->ptp_lock, flags); + + pf->hwts_ops->config_addend(pf->hw.hw_addr, port, addend); + spin_unlock_irqrestore(&pf->ptp_lock, flags); + + return 0; +} + +static int rnpm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct rnpm_adapter *pf = + container_of(ptp, struct rnpm_adapter, ptp_clock_ops); + unsigned long flags; + u32 sec, nsec; + u32 quotient, reminder; + int neg_adj = 0; + u8 port = pf->port; + + if (delta < 0) { + neg_adj = 1; + delta = -delta; + } + + if (delta == 0) + return 0; + + quotient = div_u64_rem(delta, 1000000000ULL, &reminder); + sec = quotient; + nsec = reminder; + + spin_lock_irqsave(&pf->ptp_lock, flags); + pf->hwts_ops->adjust_systime(pf->hw.hw_addr, port, sec, nsec, neg_adj); + spin_unlock_irqrestore(&pf->ptp_lock, flags); + + return 0; +} + +static int rnpm_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct rnpm_adapter *pf = + container_of(ptp, struct rnpm_adapter, ptp_clock_ops); + unsigned long flags; + u64 ns = 0; + u8 port = pf->port; + + spin_lock_irqsave(&pf->ptp_lock, flags); + + pf->hwts_ops->get_systime(pf->hw.hw_addr, port, &ns); + + spin_unlock_irqrestore(&pf->ptp_lock, flags); + + *ts = ns_to_timespec64(ns); + + return 0; +} + +static int rnpm_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct rnpm_adapter *pf = + container_of(ptp, struct rnpm_adapter, ptp_clock_ops); + unsigned long flags; + u8 port = pf->port; + + spin_lock_irqsave(&pf->ptp_lock, flags); + pf->hwts_ops->init_systime(pf->hw.hw_addr, port, ts->tv_sec, + ts->tv_nsec); + spin_unlock_irqrestore(&pf->ptp_lock, flags); + + return 0; +} + +static int rnpm_ptp_feature_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + /*TODO add support for enable the option 1588 feature PPS Auxiliary */ + return -EOPNOTSUPP; +} + +int rnpm_ptp_get_ts_config(struct rnpm_adapter *pf, struct ifreq *ifr) +{ + struct hwtstamp_config *config = &pf->tstamp_config; + + return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? -EFAULT : + 0; +} + +int rnpm_ptp_setup_ptp(struct rnpm_adapter *pf, u32 value) +{ + u32 sec_inc = 0; + u64 temp = 0; + struct timespec64 now; + u8 port = pf->port; + + /*For now just use extrnal clock(the kernel-system clock)*/ + //value |= RNPM_PTP_TCR_ESTI; + /* 1.Mask the Timestamp Trigger interrupt */ + pf->hwts_ops->config_mac_irq_enable(pf->hw.hw_addr, port, false); + /* 2.enable time stamping */ + /* 2.1 clear all bytes about time ctrl reg*/ + pf->hwts_ops->config_hw_tstamping(pf->hw.hw_addr, port, 0); + + pf->hwts_ops->config_hw_tstamping(pf->hw.hw_addr, port, value); + /* 3.Program the PTPclock frequency */ + /* program Sub Second Increment reg + * we use kernel-system clock + */ + pf->hwts_ops->config_sub_second_increment(pf->hw.hw_addr, port, + pf->clk_ptp_rate, &sec_inc); + /* 4.If use fine correction approash then, + * Program MAC_Timestamp_Addend register + */ + if (sec_inc == 0) { + ptp_dbg("%s:%d the sec_inc is zero this is a bug\n", __func__, + __LINE__); + return -EFAULT; + } + temp = div_u64(1000000000ULL, sec_inc); + /* Store sub second increment and flags for later use */ + pf->sub_second_inc = sec_inc; + pf->systime_flags = value; + /* calculate default added value: + * formula is : + * addend = (2^32)/freq_div_ratio; + * where, freq_div_ratio = 1e9ns/sec_inc + */ + temp = (u64)(temp << 32); + + if (pf->clk_ptp_rate == 0) { + pf->clk_ptp_rate = 1000; + ptp_dbg("%s:%d clk_ptp_rate is zero\n", __func__, __LINE__); + } + + pf->default_addend = div_u64(temp, pf->clk_ptp_rate); + + pf->hwts_ops->config_addend(pf->hw.hw_addr, port, pf->default_addend); + /* 5.Poll wait for the TCR Update Addend Register*/ + /* 6.enabled Fine Update method */ + /* 7.program the second and nanosecond register*/ + /*TODO If we need to enable one-step timestamp */ + + /* initialize system time */ + ktime_get_real_ts64(&now); + + /* lower 32 bits of tv_sec are safe until y2106 */ + pf->hwts_ops->init_systime(pf->hw.hw_addr, port, (u32)now.tv_sec, + now.tv_nsec); + + pf->hwts_ops->config_mac_irq_enable(pf->hw.hw_addr, port, true); + + return 0; +} + +int rnpm_ptp_set_ts_config(struct rnpm_adapter *pf, struct ifreq *ifr) +{ + struct hwtstamp_config config; + u32 ptp_v2 = 0; + u32 tstamp_all = 0; + u32 ptp_over_ipv4_udp = 0; + u32 ptp_over_ipv6_udp = 0; + u32 ptp_over_ethernet = 0; + u32 snap_type_sel = 0; + u32 ts_master_en = 0; + u32 ts_event_en = 0; + u32 value = 0; + s32 ret = -1; + u8 port = pf->port; + + if (!(pf->flags2 & RNPM_FLAG2_PTP_ENABLED)) { + pci_alert(pf->pdev, "No support for HW time stamping\n"); + pf->ptp_tx_en = 0; + pf->ptp_tx_en = 0; + + return -EOPNOTSUPP; + } + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + netdev_info(pf->netdev, + "%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n", + __func__, config.flags, config.tx_type, config.rx_filter); + /* reserved for future extensions */ + if (config.flags) + return -EINVAL; + + if (config.tx_type != HWTSTAMP_TX_OFF && + config.tx_type != HWTSTAMP_TX_ON) + return -ERANGE; + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + /* time stamp no incoming packet at all */ + config.rx_filter = HWTSTAMP_FILTER_NONE; + break; + + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + /* PTP v1, UDP, any kind of event packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + /* 'mac' hardware can support Sync, Pdelay_Req and + * Pdelay_resp by setting bit14 and bits17/16 to 01 + * This leaves Delay_Req timestamps out. + * Enable all events *and* general purpose message + * timestamping + */ + snap_type_sel = RNPM_PTP_TCR_SNAPTYPSEL_1; + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + /* PTP v1, UDP, Sync packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC; + /* take time stamp for SYNC messages only */ + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + /* PTP v1, UDP, Delay_req packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ; + /* take time stamp for Delay_Req messages only */ + ts_master_en = RNPM_PTP_TCR_TSMSTRENA; + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + /* PTP v2, UDP, any kind of event packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + + /* take time stamp for all event messages */ + snap_type_sel = RNPM_PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + /* PTP v2, UDP, Sync packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + /* take time stamp for SYNC messages only */ + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + /* PTP v2, UDP, Delay_req packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + /* take time stamp for Delay_Req messages only */ + ts_master_en = RNPM_PTP_TCR_TSMSTRENA; + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_EVENT: + /* PTP v2/802.AS1 any layer, any kind of event packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + snap_type_sel = RNPM_PTP_TCR_SNAPTYPSEL_1; + // ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + ptp_over_ethernet = RNPM_PTP_TCR_TSIPENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_SYNC: + /* PTP v2/802.AS1, any layer, Sync packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + /* take time stamp for SYNC messages only */ + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + ptp_over_ethernet = RNPM_PTP_TCR_TSIPENA; + break; + + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + /* PTP v2/802.AS1, any layer, Delay_req packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; + ptp_v2 = RNPM_PTP_TCR_TSVER2ENA; + /* take time stamp for Delay_Req messages only */ + ts_master_en = RNPM_PTP_TCR_TSMSTRENA; + ts_event_en = RNPM_PTP_TCR_TSEVNTENA; + + ptp_over_ipv4_udp = RNPM_PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = RNPM_PTP_TCR_TSIPV6ENA; + ptp_over_ethernet = RNPM_PTP_TCR_TSIPENA; + break; + +#ifdef HWTSTAMP_FILTER_NTP_ALL + case HWTSTAMP_FILTER_NTP_ALL: +#endif + case HWTSTAMP_FILTER_ALL: + /* time stamp any incoming packet */ + config.rx_filter = HWTSTAMP_FILTER_ALL; + tstamp_all = RNPM_PTP_TCR_TSENALL; + break; + + default: + return -ERANGE; + } + + pf->ptp_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1); + pf->ptp_tx_en = config.tx_type == HWTSTAMP_TX_ON; + + netdev_info( + pf->netdev, + "ptp config rx filter 0x%.2x tx_type 0x%.2x rx_en[%d] tx_en[%d]\n", + config.rx_filter, config.tx_type, pf->ptp_rx_en, pf->ptp_tx_en); + if (!pf->ptp_rx_en && !pf->ptp_tx_en) + /*rx and tx is not use hardware ts so clear the ptp register */ + pf->hwts_ops->config_hw_tstamping(pf->hw.hw_addr, port, 0); + else { + value = (RNPM_PTP_TCR_TSENA | RNPM_PTP_TCR_TSCFUPDT | + RNPM_PTP_TCR_TSCTRLSSR | tstamp_all | ptp_v2 | + ptp_over_ethernet | ptp_over_ipv6_udp | + ptp_over_ipv4_udp | ts_event_en | ts_master_en | + snap_type_sel); + ret = rnpm_ptp_setup_ptp(pf, value); + if (ret < 0) + return ret; + } + pf->ptp_config_value = value; + memcpy(&pf->tstamp_config, &config, sizeof(config)); + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : + 0; +} + +/* structure describing a PTP hardware clock */ +static struct ptp_clock_info rnpm_ptp_clock_ops = { + .owner = THIS_MODULE, + .name = "rnpm ptp", + .max_adj = 500000000, + .n_alarm = 0, + .n_ext_ts = 0, + .n_per_out = 0, /* will be overwritten in stmmac_ptp_register */ + .n_pins = 0, /*should be 0 if not set*/ + .adjfine = rnpm_ptp_adjfine, + .adjtime = rnpm_ptp_adjtime, + .gettime64 = rnpm_ptp_gettime, + .settime64 = rnpm_ptp_settime, + .enable = rnpm_ptp_feature_enable, +}; + +int rnpm_ptp_register(struct rnpm_adapter *pf) +{ + pf->hwts_ops = &mac_ptp; + + pf->ptp_tx_en = 0; + pf->ptp_rx_en = 0; + + spin_lock_init(&pf->ptp_lock); + pf->flags2 |= RNPM_FLAG2_PTP_ENABLED; + pf->ptp_clock_ops = rnpm_ptp_clock_ops; + + /*default mac clock rate is 50Mhz */ + pf->clk_ptp_rate = 50000000; //50Mhz + if (pf->pdev == NULL) + ptp_dbg("pdev dev is null\n"); + + pf->ptp_clock = ptp_clock_register(&pf->ptp_clock_ops, &pf->pdev->dev); + if (pf->ptp_clock == NULL) + pci_err(pf->pdev, "ptp clock register failed\n"); + + if (IS_ERR(pf->ptp_clock)) { + pci_err(pf->pdev, "ptp_clock_register failed\n"); + pf->ptp_clock = NULL; + } else { + pci_info(pf->pdev, "registered PTP clock\n"); + } + + return 0; +} + +void rnpm_ptp_unregister(struct rnpm_adapter *pf) +{ + /*1. stop the ptp module*/ + if (pf->ptp_clock) { + ptp_clock_unregister(pf->ptp_clock); + pf->ptp_clock = NULL; + pr_debug("Removed PTP HW clock successfully on %s\n", + "rnpm_ptp"); + } +} + +#if defined(DEBUG_PTP_HARD_SOFTWAY_RX) || defined(DEBUG_PTP_HARD_SOFTWAY_TX) +static u64 rnpm_get_software_ts(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return (ts.tv_nsec + ts.tv_sec * 1000000000ULL); +} +#endif + +#if defined(DEBUG_PTP_TX_TIMESTAMP) || defined(DEBUG_PTP_RX_TIMESTAMP) +#define TIME_ZONE_CHINA (8) +char *asctime(const struct tm *timeptr) +{ + static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" }; + static const char mon_name[][4] = { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + static char result[26]; + + sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %ld\n", + wday_name[timeptr->tm_wday], mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour + TIME_ZONE_CHINA, + timeptr->tm_min, timeptr->tm_sec, 1900 + timeptr->tm_year); + return result; +} + +static void rnpm_print_human_timestamp(uint64_t ns, uint8_t *direct) +{ + struct timespec64 ts; + struct tm tms; + ktime_t ktm = ns_to_ktime(ns); + + ts = ktime_to_timespec64(ktm); + //time64_to_tm(ts.tv_sec, ts.tv_nsec / 1000000000ULL, &tms); + //ptp_dbg("[%s] %s ------\n", direct, asctime(&tms)); + ptp_dbg("[%s] %s ------\n", direct, ns); +} +#endif + +void rnpm_tx_hwtstamp_work(struct work_struct *work) +{ + struct rnpm_adapter *adapter = + container_of(work, struct rnpm_adapter, tx_hwtstamp_work); + void __iomem *ioaddr = adapter->hw.hw_addr; + + /* 1. read port belone timestatmp status reg */ + /* 2. status enabled read nsec and sec reg*/ + /* 3. */ + u64 nanosec = 0, sec = 0; + u8 port = adapter->port; + + if (!adapter->ptp_tx_skb) + return; + + if (rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_TSVALUE_STATUS(port)) & 0x01) { + struct sk_buff *skb = adapter->ptp_tx_skb; + struct skb_shared_hwtstamps shhwtstamps; + u64 txstmp = 0; + /* read and add nsec, sec turn to nsec*/ + + nanosec = rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_LTIMES(port)); + sec = rnpm_rd_reg(ioaddr + RNPM_ETH_PTP_TX_HTIMES(port)); + /* when we read the timestamp finish need to notice the hardware + * that the timestamp need to update via set tx_hwts_clear-reg + * from high to low + */ + //printk("port %d call clean ptp %llx %llx\n", port, nanosec, sec); + + rnpm_wr_reg(ioaddr + RNPM_ETH_PTP_TX_CLEAR(port), + PTP_GET_TX_HWTS_FINISH); + rnpm_wr_reg(ioaddr + RNPM_ETH_PTP_TX_CLEAR(port), + PTP_GET_TX_HWTS_UPDATE); + txstmp = nanosec & PTP_HWTX_TIME_VALUE_MASK; + txstmp += (sec & PTP_HWTX_TIME_VALUE_MASK) * 1000000000ULL; + + /* Clear the global tx_hwtstamp_skb pointer and force writes + * prior to notifying the stack of a Tx timestamp. + */ + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ns_to_ktime(txstmp); + adapter->ptp_tx_skb = NULL; + clear_bit_unlock(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state); +#ifdef DEBUG_PTP_TX_TIMESTAMP + rnpm_print_human_timestamp(txstmp, "TX"); +#endif + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + */ + wmb(); + /* force write prior to skb_tstamp_tx + * because the xmit will re used the point to store ptp skb + */ + skb_tstamp_tx(skb, &shhwtstamps); + dev_consume_skb_any(skb); + } else if (time_after(jiffies, + adapter->tx_hwtstamp_start + + adapter->tx_timeout_factor * HZ)) { + /* this function will mark the skb drop*/ + if (adapter->ptp_tx_skb) + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + adapter->tx_hwtstamp_timeouts++; + clear_bit_unlock(__RNPM_PTP_TX_IN_PROGRESS, &adapter->state); + netdev_warn(adapter->netdev, "clearing Tx timestamp hang\n"); + } else { + /* reschedule to check later */ +#ifdef DEBUG_PTP_HARD_SOFTWAY_TX + struct skb_shared_hwtstamps shhwtstamp; + u64 ns = 0; + + ns = rnpm_get_software_ts(); + shhwtstamp.hwtstamp = ns_to_ktime(ns); + if (adapter->ptp_tx_skb) { + skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamp); + dev_consume_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + } +#else + schedule_work(&adapter->tx_hwtstamp_work); +#endif + } +} + +void rnpm_ptp_get_rx_hwstamp(struct rnpm_adapter *adapter, + union rnpm_rx_desc *desc, struct sk_buff *skb) +{ + u64 ns = 0; + u64 tsvalueh = 0, tsvaluel = 0; + struct skb_shared_hwtstamps *hwtstamps = NULL; + + if (!skb || !adapter->ptp_rx_en) { + netdev_dbg(adapter->netdev, + "hwstamp skb is null or rx_en is zero %u\n", + adapter->ptp_rx_en); + return; + } + +#ifdef DEBUG_PTP_HARD_SOFTWAY_RX + ns = rnpm_get_software_ts(); +#else + if (likely(!((desc->wb.cmd) & RNPM_RXD_STAT_PTP))) + return; + hwtstamps = skb_hwtstamps(skb); + /* because of rx hwstamp store before the mac head + * skb->head and skb->data is point to same location when call alloc_skb + * so we must move 16 bytes the skb->data to the mac head location + * but for the head point if we need move the skb->head need to be diss + */ + /* low8bytes is null high8bytes is timestamp + * high32bit is seconds low32bits is nanoseconds + */ + skb_copy_from_linear_data_offset(skb, RNPM_RX_TIME_RESERVE, &tsvalueh, + RNPM_RX_SEC_SIZE); + skb_copy_from_linear_data_offset( + skb, RNPM_RX_TIME_RESERVE + RNPM_RX_SEC_SIZE, &tsvaluel, + RNPM_RX_NANOSEC_SIZE); + skb_pull(skb, RNPM_RX_HWTS_OFFSET); + tsvalueh = ntohl(tsvalueh); + tsvaluel = ntohl(tsvaluel); + + ns = tsvaluel & RNPM_RX_NSEC_MASK; + ns += ((tsvalueh & RNPM_RX_SEC_MASK) * 1000000000ULL); + netdev_dbg(adapter->netdev, + "ptp get hardware ts-sec %llu ts-nanosec %llu\n", tsvalueh, + tsvaluel); + hwtstamps->hwtstamp = ns_to_ktime(ns); +#endif +#ifdef DEBUG_PTP_RX_TIMESTAMP + rnpm_print_human_timestamp(ns, "RX"); +#endif +} + +void rnpm_ptp_reset(struct rnpm_adapter *adapter) +{ + rnpm_ptp_setup_ptp(adapter, adapter->ptp_config_value); +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.h b/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.h new file mode 100644 index 000000000000..76025c9ed352 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_ptp.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef __RNPM_PTP_H__ +#define __RNPM_PTP_H__ + +/* PTP Timestamp control register defines */ +/* Timestamp Enable */ +#define RNPM_PTP_TCR_TSENA BIT(0) +/* Timestamp Fine/Coarse Update */ +#define RNPM_PTP_TCR_TSCFUPDT BIT(1) +/* Timestamp Initialize */ +#define RNPM_PTP_TCR_TSINIT BIT(2) +/* Timestamp Update */ +#define RNPM_PTP_TCR_TSUPDT BIT(3) +/* Timestamp Interrupt Trigger Enable */ +#define RNPM_PTP_TCR_TSTRIG BIT(4) +/* Addend Reg Update */ +#define RNPM_PTP_TCR_TSADDREG BIT(5) +/* Enable Timestamp for All Frames */ +#define RNPM_PTP_TCR_TSENALL BIT(8) +/* Digital or Binary Rollover Control */ +#define RNPM_PTP_TCR_TSCTRLSSR BIT(9) +/* Enable PTP packet Processing for Version 2 Format */ +#define RNPM_PTP_TCR_TSVER2ENA BIT(10) +/* Enable Processing of PTP over Ethernet Frames */ +#define RNPM_PTP_TCR_TSIPENA BIT(11) +/* Enable Processing of PTP Frames Sent over IPv6-UDP */ +#define RNPM_PTP_TCR_TSIPV6ENA BIT(12) +/* Enable Processing of PTP Frames Sent over IPv4-UDP */ +#define RNPM_PTP_TCR_TSIPV4ENA BIT(13) +/* Enable Timestamp Snapshot for Event Messages */ +#define RNPM_PTP_TCR_TSEVNTENA BIT(14) +/* Enable Snapshot for Messages Relevant to Master */ +#define RNPM_PTP_TCR_TSMSTRENA BIT(15) +/* Note 802.1 AS Is work Over Ethernet FramesC_Sub_Second_Incremen + * and Normal PTP Is work Oveer UDP + */ + +/* Select PTP packets for Taking Snapshots + * On mac specifically: + * Enable SYNC, Pdelay_Req, Pdelay_Resp when TSEVNTENA is enabled. + * or + * Enable SYNC, Follow_Up, Delay_Req, Delay_Resp, Pdelay_Req, Pdelay_Resp, + * Pdelay_Resp_Follow_Up if TSEVNTENA is disabled + */ +#define RNPM_PTP_TCR_SNAPTYPSEL_1 BIT(16) +/* Enable MAC address for PTP Frame Filtering */ +#define RNPM_PTP_TCR_TSENMACADDR BIT(18) +/* External System Time Input Or MAC Internal Clock*/ +#define RNPM_PTP_TCR_ESTI BIT(20) +/* AV802.1 AS Mode Enable*/ +#define RNPM_PTP_TCR_AV8021ASMEN BIT(28) +/* Sub Second increament define */ +/* Sub-second increment value */ +#define RNPM_PTP_SSIR_SSINC_MASK (0xff) +/* Sub-second increment offset */ +#define RNPM_PTP_SSIR_SSINC_SHIFT (16) +/* TX timestamp reg is fill complete */ +#define RNPM_MAC_TXTSC BIT(15) +/*nano second avalid value */ +#define RNPM_MAC_TXTSSTSLO GENMASK(30, 0) + +#define RNPM_RX_SEC_MASK GENMASK(30, 0) +#define RNPM_RX_NSEC_MASK GENMASK(30, 0) +#define RNPM_RX_TIME_RESERVE (8) +#define RNPM_RX_SEC_SIZE (4) +#define RNPM_RX_NANOSEC_SIZE (4) +#define RNPM_RX_HWTS_OFFSET \ + (RNPM_RX_SEC_SIZE + RNPM_RX_NANOSEC_SIZE + RNPM_RX_TIME_RESERVE) + +#define PTP_STNSUR_ADDSUB_SHIFT (31) +/* 10e9-1 ns */ +#define PTP_DIGITAL_ROLLOVER_MODE 0x3B9AC9ff +/* ns */ +#define PTP_BINARY_ROLLOVER_MODE 0x7FFFFFFF + +#define PTP_HWTX_TIME_VALUE_MASK GENMASK(31, 0) +#define PTP_GET_TX_HWTS_FINISH (1) +#define PTP_GET_TX_HWTS_UPDATE (0) +/*hardware ts can't so fake ts from the software clock */ +#define DEBUG_PTP_HARD_SOFTWAY + +int rnpm_ptp_get_ts_config(struct rnpm_adapter *pf, struct ifreq *ifr); +int rnpm_ptp_set_ts_config(struct rnpm_adapter *pf, struct ifreq *ifr); +int rnpm_ptp_register(struct rnpm_adapter *pf); +void rnpm_ptp_unregister(struct rnpm_adapter *pf); + +void rnpm_ptp_get_rx_hwstamp(struct rnpm_adapter *pf, union rnpm_rx_desc *desc, + struct sk_buff *skb); +void rnpm_tx_hwtstamp_work(struct work_struct *work); +void rnpm_ptp_reset(struct rnpm_adapter *adapter); +#endif diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_regs.h b/drivers/net/ethernet/mucse/rnpm/rnpm_regs.h new file mode 100644 index 000000000000..f3958429c289 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_regs.h @@ -0,0 +1,667 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef RNPM_REGS_H +#define RNPM_REGS_H + +/* BAR4 memory */ +/* ------------------------------------------*/ +/* module | size | start | end */ +/* DMA | 64KB | 0_0000H | 0_FFFFH */ +/* ETH | 64KB | 1_0000H | 1_FFFFH */ +/* REG | 64KB | 3_0000H | 3_FFFFH */ +/* SERDES | 128KB | 4_0000H | 5_FFFFH */ +/* XLMAC1 | 64KB | 6_0000H | 6_FFFFH */ +/* XLMAC2 | 64KB | 7_0000H | 7_FFFFH */ +/* XLMAC3 | 64KB | 8_0000H | 8_FFFFH */ +/* XLMAC4 | 64KB | 9_0000H | 9_FFFFH */ +/* MSIX | 64KB | A_0000H | A_FFFFH */ +/* SWITCH | 64KB | B_0000H | B_FFFFH */ +/* TCAM | 256KB | C_0000H | F_FFFFH */ +/* ------------------------------------------*/ + +/* ==================== RNPM-DMA Global Registers ==================== */ +#define RNPM_DMA_VERSION (0x0000) +#define RNPM_DMA_CONFIG (0x0004) +#define DMA_MAC_LOOPBACK (1 << 0) +#define DMA_SWITCH_LOOPBACK (1 << 1) +#define DMA_VEB_BYPASS (1 << 4) +#define DMA_AXI_ORDER (1 << 5) +#define DMA_RX_PADDING (1 << 8) +#define DMA_MAP_MODE(n) (n << 12) +#define DMA_RX_FRAGMENT_BYTES(n) (((n) / 16) << 16) +#define RNPM_DMA_STATUS (0x0008) +#define DMA_RING_NUM (0xff << 24) +#define RNPM_DMA_DUMY (0x000c) +/* RNPM-DMA AXI Register */ +#define RNPM_DMA_AXI_EN (0x0010) +#define RX_AXI_RW_EN (0x3 << 0) +#define TX_AXI_RW_EN (0x3 << 2) +#define RNPM_DMA_AXI_STAT (0x0014) +#define RNPM_VEB_MAC_MASK_LO (0x0020) +#define RNPM_VEB_MAC_MASK_HI (0x0024) +#define RNPM_VEB_VLAN_MASK (0x0028) +#define DEBUG_PROBE_NUM (16) +#define RNPM_DMA_DEBUG_PROBE_LO_REG(n) (0x0100 + 0x08 * (n)) +#define RNPM_DMA_DEBUG_PROBE_HI_REG(n) (0x0100 + 0x08 * (n)) +#define DEBUG_CNT_NUM (76) +#define RNPM_DMA_DEBUG_CNT(n) (0x0200 + 0x04 * (n)) +#define RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL(i) \ + (RNPM_DMA_DEBUG_CNT(17) + 0x04 * (i)) +#define RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_0 (RNPM_DMA_DEBUG_CNT(17)) +#define RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_1 (RNPM_DMA_DEBUG_CNT(18)) +#define RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_2 (RNPM_DMA_DEBUG_CNT(19)) +#define RNPM_DMA_STATS_DMA_TO_DMA_CHANNEL_3 (RNPM_DMA_DEBUG_CNT(20)) +#define RNPM_DMA_STATS_DMA_TO_SWITCH (RNPM_DMA_DEBUG_CNT(21)) +#define RNPM_DMA_STATS_MAC_TO_DMA (RNPM_DMA_DEBUG_CNT(22)) +#define RNPM_DMA_STATS_SWITCH_TO_DMA (RNPM_DMA_DEBUG_CNT(23)) +#define RNPM_PCI_WR_TO_HOST (RNPM_DMA_DEBUG_CNT(34)) +/* DMA-ENABLE-IRQ */ +#define RNPM_DMA_RX_START(idx) (0x8010 + 0x100 * (idx)) +#define RNPM_DMA_RX_READY(idx) (0x8014 + 0x100 * (idx)) +#define RNPM_DMA_TX_START(idx) (0x8018 + 0x100 * (idx)) +// #define RNPM_DMA_TX_START(idx) (0x000c) +#define RNPM_DMA_TX_READY(idx) (0x801c + 0x100 * (idx)) +#define RNPM_DMA_INT_STAT(idx) (0x8020 + 0x100 * (idx)) +#define RNPM_DMA_INT_MASK(idx) (0x8024 + 0x100 * (idx)) +#define TX_INT_MASK (1 << 1) +#define RX_INT_MASK (1 << 0) +#define RNPM_DMA_INT_CLR(idx) (0x8028 + 0x100 * (idx)) +/* RX-Queue Registers */ +#define RNPM_DMA_REG_RX_DESC_BUF_BASE_ADDR_HI(idx) (0x8030 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_DESC_BUF_BASE_ADDR_LO(idx) (0x8034 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_DESC_BUF_LEN(idx) (0x8038 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_DESC_BUF_HEAD(idx) (0x803c + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_DESC_BUF_TAIL(idx) (0x8040 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_DESC_FETCH_CTRL(idx) (0x8044 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_INT_DELAY_TIMER(idx) (0x8048 + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_INT_DELAY_PKTCNT(idx) (0x804c + 0x100 * (idx)) +#define RNPM_DMA_REG_RX_ARB_DEF_LVL(idx) (0x8050 + 0x100 * (idx)) +#define PCI_DMA_REG_RX_DESC_TIMEOUT_TH(idx) (0x8054 + 0x100 * (idx)) +/* TX-Queue Registers */ +#define RNPM_DMA_REG_TX_DESC_BUF_BASE_ADDR_HI(idx) (0x8060 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_DESC_BUF_BASE_ADDR_LO(idx) (0x8064 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_DESC_BUF_LEN(idx) (0x8068 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_DESC_BUF_HEAD(idx) (0x806c + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_DESC_BUF_TAIL(idx) (0x8070 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_DESC_FETCH_CTRL(idx) (0x8074 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_INT_DELAY_TIMER(idx) (0x8078 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_INT_DELAY_PKTCNT(idx) (0x807c + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_ARB_DEF_LVL(idx) (0x8080 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_FLOW_CTRL_TH(idx) (0x8084 + 0x100 * (idx)) +#define RNPM_DMA_REG_TX_FLOW_CTRL_TM(idx) (0x8088 + 0x100 * (idx)) +/* VEB Registers */ +#define VEB_TBL_CNTS (64) +#define RNPM_DMA_PORT_VBE_MAC_LO_TBL(port, vf) \ + (0x80A0 + 4 * (port) + 0x100 * (vf)) +#define RNPM_DMA_PORT_VBE_MAC_HI_TBL(port, vf) \ + (0x80B0 + 4 * (port) + 0x100 * (vf)) +#define RNPM_DMA_PORT_VEB_VID_TBL(port, vf) (0x80C0 + 4 * (port) + 0x100 * (vf)) +#define RNPM_DMA_PORT_VEB_VF_RING_TBL(port, vf) \ + (0x80D0 + 4 * (port) + 0x100 * (vf)) +/* ================================================================== */ + +/* ==================== RNPM-ETH Global Registers ==================== */ +#define RNPM_ETH_BASE (0x10000) + +#define ETH_ERR_SCTP (1 << 4) +#define ETH_ERR_L4 (1 << 3) +#define ETH_ERR_L3 (1 << 2) +#define ETH_ERR_PKT_LEN_ERR (1 << 1) +#define ETH_ERR_HDR_LEN_ERR (1 << 0) +#define ETH_IGNORE_ALL_ERR \ + (ETH_ERR_SCTP | ETH_ERR_L4 | ETH_ERR_L3 | ETH_ERR_PKT_LEN_ERR | \ + ETH_ERR_HDR_LEN_ERR) +#define VM_DMAC_TBL_SZ (128) +#define RNPM_ETH_ENABLE_RSS_ONLY (0x3f30001) +#define RNPM_ETH_DISABLE_RSS (0) + +#define RNPM_ETH_TX_PROGFULL_THRESH_PORT(n) \ + (RNPM_ETH_BASE + 0x0060 + 0x08 * (n)) +#define RNPM_ETH_TX_PROGEMPTY_THRESH_PORT(n) \ + (RNPM_ETH_BASE + 0x0064 + 0x08 * (n)) + +#define RNPM_ETH_EMAC_DMA_PROFULL_THRESH (RNPM_ETH_BASE + 0x0080) +#define RNPM_ETH_EMAC_DMA_PROEMPTY_THRESH (RNPM_ETH_BASE + 0x0084) +#define RNPM_ETH_EMAC_SW_PROFULL_THRESH (RNPM_ETH_BASE + 0x0088) +#define RNPM_ETH_EMAC_SW_PROEMPTY_THRESH (RNPM_ETH_BASE + 0x008c) +#define RNPM_ETH_EMAC_BMC_TX_PROFULL_THRESH (RNPM_ETH_BASE + 0x0090) +#define RNPM_ETH_EMAC_BMC_TX_PROEMPTY_THRESH (RNPM_ETH_BASE + 0x0094) + +#define RNPM_ETH_CNT_PKT_EMAC_TX(n) (RNPM_ETH_BASE + 0x00a0 + 0x04 * (n)) +#define RNPM_ETH_CNT_PKT_PECL_TX(n) (RNPM_ETH_BASE + 0x00b0 + 0x04 * (n)) +#define RNPM_ETH_STATUS_TX_FLOWCTRL(n) (RNPM_ETH_BASE + 0x00c0 + 0x04 * (n)) +#define RNPM_ETH_VERSION_FLOWWCTRL (RNPM_ETH_BASE + 0x00d0) +#define RNPM_ETH_CFG_ETH_MAC (RNPM_ETH_BASE + 0x00d4) + +#define RNPM_ETH_SCA_TX_CS(port) (RNPM_ETH_BASE + 0x0100 + 0x08 * (port)) +#define RNPM_ETH_SCA_TX_NS(port) (RNPM_ETH_BASE + 0x0104 + 0x08 * (port)) +#define RNPM_ETH_TXTRANS_CS(port) (RNPM_ETH_BASE + 0x0120 + 0x08 * (port)) +#define RNPM_ETH_TXTRANS_NS(port) (RNPM_ETH_BASE + 0x0124 + 0x08 * (port)) + +#define RNPM_ETH_1TO4_INST0_IN_PKTS (RNPM_ETH_BASE + 0x0200) +#define RNPM_ETH_1TO4_INST1_IN_PKTS (RNPM_ETH_BASE + 0x0204) +#define RNPM_ETH_1TO4_INST2_IN_PKTS (RNPM_ETH_BASE + 0x0208) +#define RNPM_ETH_1TO4_INST3_IN_PKTS (RNPM_ETH_BASE + 0x020c) +#define RNPM_ETH_IN_0_TX_PKT_NUM(port) (RNPM_ETH_BASE + 0x0210 + 0x10 * (port)) +#define RNPM_ETH_IN_1_TX_PKT_NUM(port) (RNPM_ETH_BASE + 0x0214 + 0x10 * (port)) +#define RNPM_ETH_IN_2_TX_PKT_NUM(port) (RNPM_ETH_BASE + 0x0218 + 0x10 * (port)) +#define RNPM_ETH_IN_3_TX_PKT_NUM(port) (RNPM_ETH_BASE + 0x021c + 0x10 * (port)) + +#define RNPM_ETH_EMAC_TX_TO_PHY_PKTS(port) (RNPM_ETH_BASE + 0x0250 + 4 * (port)) +#define RNPM_ETH_TXTRANS_PTP_PKT_NUM(port) (RNPM_ETH_BASE + 0x0260 + 4 * (port)) + +#define RNPM_ETH_TX_DEBUG(n) (RNPM_ETH_BASE + 0x0300 + 0x04 * (n)) + +#define RNPM_ETH_TX_DEBUG_PORT0_SOP (RNPM_ETH_BASE + 0x0300) +#define RNPM_ETH_TX_DEBUG_PORT0_EOP (RNPM_ETH_BASE + 0x0304) + +#define RNPM_ETH_TX_DEBUG_PORT1_SOP (RNPM_ETH_BASE + 0x0308) +#define RNPM_ETH_TX_DEBUG_PORT1_EOP (RNPM_ETH_BASE + 0x030c) + +#define RNPM_ETH_TX_DEBUG_PORT2_SOP (RNPM_ETH_BASE + 0x0310) +#define RNPM_ETH_TX_DEBUG_PORT2_EOP (RNPM_ETH_BASE + 0x0314) + +#define RNPM_ETH_TX_DEBUG_PORT3_SOP (RNPM_ETH_BASE + 0x0318) +#define RNPM_ETH_TX_DEBUG_PORT3_EOP (RNPM_ETH_BASE + 0x031c) + +#define RNPM_ETH_TX_DEBUG_EMPTY (RNPM_ETH_BASE + 0x0334) +#define RNPM_ETH_TX_DEBUG_PROG_FULL (RNPM_ETH_BASE + 0x0338) +#define RNPM_ETH_TX_DEBUG_FULL (RNPM_ETH_BASE + 0x033c) + +/* 1588 */ +#define RNPM_ETH_PTP_TX_STATUS(n) (RNPM_ETH_BASE + 0x0400 + 0x14 * (n)) +#define RNPM_ETH_PTP_TX_HTIMES(n) (RNPM_ETH_BASE + 0x0404 + 0x14 * (n)) +#define RNPM_ETH_PTP_TX_LTIMES(n) (RNPM_ETH_BASE + 0x0408 + 0x14 * (n)) +#define RNPM_ETH_PTP_TX_TSVALUE_STATUS(n) (RNPM_ETH_BASE + 0x040c + 0x14 * (n)) +#define RNPM_ETH_PTP_TX_CLEAR(n) (RNPM_ETH_BASE + 0x0410 + 0x14 * (n)) + +#define RNPM_ETH_MAC_SPEED_PORT(n) (RNPM_ETH_BASE + 0x0450 + 0x04 * (n)) +#define RNPM_ETH_MAC_LOOPBACK_MODE_PORT(n) (RNPM_ETH_BASE + 0x0460 + 0x04 * (n)) +#define RNPM_ETH_EXCEPT_DROP_PROC (RNPM_ETH_BASE + 0x0470) + +#define RNPM_ETH_IPP (RNPM_ETH_BASE + 0x8000) +#define RNPM_ETH_BYPASS (RNPM_ETH_BASE + 0x8000) +#define RNPM_ETH_TUNNEL_MOD (RNPM_ETH_BASE + 0x8004) +#define INNER_L4_BIT BIT(6) +#define PKT_LEN_ERR (2) +#define HDR_LEN_ERR (1) +#define RNPM_ETH_LOOPBACK_EN (RNPM_ETH_BASE + 0x8008) +#define RNPM_FIFO_CTRL_MODE (RNPM_ETH_BASE + 0x800c) +#define RNPM_ETH_VXLAN_PORT (RNPM_ETH_BASE + 0x8010) +#define RNPM_ETH_NVGRE_PORT (RNPM_ETH_BASE + 0x8014) +#define RNPM_ETH_RDMA_PORT (RNPM_ETH_BASE + 0x8018) +#define RNPM_HOST_FILTER_EN (RNPM_ETH_BASE + 0x801c) +#define RNPM_MNG_FILTER_EN (RNPM_ETH_BASE + 0x8020) +#define RNPM_ETH_TCAM_EN (RNPM_ETH_BASE + 0x8024) +#define RNPM_CONGEST_DROP_EN (RNPM_ETH_BASE + 0x8028) +#define RNPM_REDIR_EN (RNPM_ETH_BASE + 0x8030) +#define RNPM_ETH_SCTP_CHECKSUM_EN (RNPM_ETH_BASE + 0x8038) +#define RNPM_ETH_ARP_FUNC_EN (RNPM_ETH_BASE + 0x803c) +#define RNPM_ETH_VLAN_VME_REG(n) (RNPM_ETH_BASE + 0x8040 + 0x04 * (n)) +#define RNPM_ETH_CVLAN_RM_EN (RNPM_ETH_BASE + 0x8050) +#define RNPM_ETH_VLAN_RM_TYPE (RNPM_ETH_BASE + 0x8054) +#define RNPM_ETH_WRAP_FIELD_TYPE (RNPM_ETH_BASE + 0x805c) +#define RNPM_ETH_ERR_MASK_VECTOR (RNPM_ETH_BASE + 0x8060) +#define RNPM_ETH_DEFAULT_RX_RING (RNPM_ETH_BASE + 0x806c) + +#define DROP_ALL_THRESH (2046) // drop all rx +#define RECEIVE_ALL_THRESH (0x270) // receive all rx + +#define RNPM_ETH_RX_PROGFULL_THRESH_PORT(n) \ + (RNPM_ETH_BASE + 0x8070 + 0x08 * (n)) +#define RNPM_ETH_RX_PROGEMPTY_THRESH_PORT(n) \ + (RNPM_ETH_BASE + 0x8074 + 0x08 * (n)) + +#define RNPM_ETH_EMAC_GAT_PROGFULL_THRESH (RNPM_ETH_BASE + 0x8090) +#define RNPM_ETH_EMAC_GAT_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x8094) +#define RNPM_ETH_EMAC_PARSE_PROGFULL_THRESH (RNPM_ETH_BASE + 0x8098) +#define RNPM_ETH_EMAC_PARSE_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x809c) +#define RNPM_ETH_FC_PROGFULL_THRESH (RNPM_ETH_BASE + 0x80a0) +#define RNPM_ETH_FC_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x80a4) +#define RNPM_ETH_DIS_PROGFULL_THRESH (RNPM_ETH_BASE + 0x80a8) +#define RNPM_ETH_DIS_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x80ac) +#define RNPM_ETH_COV_PROGFULL_THRESH (RNPM_ETH_BASE + 0x80b0) +#define RNPM_ETH_COV_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x80b4) +#define RNPM_ETH_BMC_RX_PROGFULL_THRESH (RNPM_ETH_BASE + 0x80b8) +#define RNPM_ETH_BMC_RX_PROGEMPTY_THRESH (RNPM_ETH_BASE + 0x80bc) +#define RNPM_ETH_HIGH_WATER(n) (RNPM_ETH_BASE + 0x80c0 + n * (0x08)) +#define RNPM_ETH_LOW_WATER(n) (RNPM_ETH_BASE + 0x80c4 + n * (0x08)) +#define RNPM_ETH_DEFAULT_RX_MIN_LEN (RNPM_ETH_BASE + 0x80f0) +#define RNPM_ETH_DEFAULT_RX_MAX_LEN (RNPM_ETH_BASE + 0x80f4) +#define RNPM_ETH_PTP_EVENT_PORT (RNPM_ETH_BASE + 0x80f8) +#define RNPM_ETH_PTP_GENER_PORT_REG (RNPM_ETH_BASE + 0x80fc) +#define RNPM_ETH_RX_TRANS_CS_PORT(n) (RNPM_ETH_BASE + 0x8100 + 0x08 * (n)) +#define RNPM_ETH_RX_TRANS_NS_PORT(n) (RNPM_ETH_BASE + 0x8104 + 0x08 * (n)) + +#define RNPM_ETH_GAT_RX_CS (RNPM_ETH_BASE + 0x8120) +#define RNPM_ETH_GAT_RX_NS (RNPM_ETH_BASE + 0x8124) +#define RNPM_ETH_EMAC_PIP_CS (RNPM_ETH_BASE + 0x8128) +#define RNPM_ETH_EMAC_PIP_NS (RNPM_ETH_BASE + 0x812c) +#define RNPM_ETH_EMAC_FC_CS (RNPM_ETH_BASE + 0x8138) +#define RNPM_ETH_EMAC_FC_NS (RNPM_ETH_BASE + 0x813c) +#define RNPM_ETH_EMAC_DIS_CS (RNPM_ETH_BASE + 0x8140) +#define RNPM_ETH_EMAC_DIS_NS (RNPM_ETH_BASE + 0x8144) +#define RNPM_ETH_HOST_L2_FILTER_CS (RNPM_ETH_BASE + 0x8150) +#define RNPM_ETH_HOST_L2_FILTER_NS (RNPM_ETH_BASE + 0x8154) +#define RNPM_ETH_EMAC_DECAP_CS (RNPM_ETH_BASE + 0x8158) +#define RNPM_ETH_EMAC_DECAP_NS (RNPM_ETH_BASE + 0x815c) + +#define RNPM_ETH_PFC_CONFIG_PROT(n) (RNPM_ETH_BASE + 0x8180 + n * (0x04)) + +#define RNPM_ETH_RX_PKT_NUM(port) (RNPM_ETH_BASE + 0x8220 + 0x04 * (port)) +#define RNPM_ETH_RX_DROP_PKT_NUM(port) (RNPM_ETH_BASE + 0x8230 + 0x04 * (port)) +#define RNPM_ETH_TOTAL_GAT_RX_PKT_NUM (RNPM_ETH_BASE + 0x8240) +#define RNPM_ETH_PKT_ARP_REQ_NUM (RNPM_ETH_BASE + 0x8250) +#define RNPM_ETH_PKT_ARP_RESPONSE_NUM (RNPM_ETH_BASE + 0x8254) +#define RNPM_ETH_ICMP_NUM (RNPM_ETH_BASE + 0x8258) +#define RNPM_ETH_PKT_UDP_NUM (RNPM_ETH_BASE + 0x825c) +#define RNPM_ETH_PKT_TCP_NUM (RNPM_ETH_BASE + 0x8260) +#define RNPM_ETH_PKT_ESP_NUM (RNPM_ETH_BASE + 0x8264) +#define RNPM_ETH_PKT_GRE_NUM (RNPM_ETH_BASE + 0x8268) +#define RNPM_ETH_PKT_SCTP_NUM (RNPM_ETH_BASE + 0x826c) +#define RNPM_ETH_PKT_TCPSYN_NUM (RNPM_ETH_BASE + 0x8270) +#define RNPM_ETH_PKT_VXLAN_NUM (RNPM_ETH_BASE + 0x8274) +#define RNPM_ETH_PKT_NVGRE_NUM (RNPM_ETH_BASE + 0x8278) +#define RNPM_ETH_PKT_FRAGMENT_NUM (RNPM_ETH_BASE + 0x827c) +#define RNPM_ETH_PKT_LAYER1_VLAN_NUM (RNPM_ETH_BASE + 0x8280) +#define RNPM_ETH_PKT_LAYER2_VLAN_NUM (RNPM_ETH_BASE + 0x8284) +#define RNPM_ETH_PKT_IPV4_NUM (RNPM_ETH_BASE + 0x8288) +#define RNPM_ETH_PKT_IPV6_NUM (RNPM_ETH_BASE + 0x828c) +#define RNPM_ETH_PKT_INGRESS_NUM (RNPM_ETH_BASE + 0x8290) +#define RNPM_ETH_PKT_EGRESS_NUM (RNPM_ETH_BASE + 0x8294) +#define RNPM_ETH_PKT_IP_HDR_LEN_ERR_NUM (RNPM_ETH_BASE + 0x8298) +#define RNPM_ETH_PKT_IP_PKT_LEN_ERR_NUM (RNPM_ETH_BASE + 0x829c) +#define RNPM_ETH_PKT_L3_HDR_CHK_ERR_NUM (RNPM_ETH_BASE + 0x82a0) +#define RNPM_ETH_PKT_L4_HDR_CHK_ERR_NUM (RNPM_ETH_BASE + 0x82a4) +#define RNPM_ETH_PKT_SCTP_CHK_ERR_NUM (RNPM_ETH_BASE + 0x82a8) +#define RNPM_ETH_PKT_VLAN_ERR_NUM (RNPM_ETH_BASE + 0x82ac) +#define RNPM_ETH_PKT_RDMA_NUM (RNPM_ETH_BASE + 0x82b0) +#define RNPM_ETH_PKT_ARP_AUTO_RESPONSE_NUM (RNPM_ETH_BASE + 0x82b4) +#define RNPM_ETH_PKT_ICMPV6_NUM (RNPM_ETH_BASE + 0x82b8) +#define RNPM_ETH_PKT_IPV6_EXTEND_NUM (RNPM_ETH_BASE + 0x82bc) +#define RNPM_ETH_PKT_802_3_NUM (RNPM_ETH_BASE + 0x82c0) +#define RNPM_ETH_PKT_EXCEPT_SHORT_NUM (RNPM_ETH_BASE + 0x82c4) +#define RNPM_ETH_PKT_PTP_NUM (RNPM_ETH_BASE + 0x82c8) +#define RNPM_ETH_DECAP_PKT_IN_NUM (RNPM_ETH_BASE + 0x82d0) +#define RNPM_ETH_DECAP_PKT_OUT_NUM (RNPM_ETH_BASE + 0x82d4) +#define RNPM_ETH_DECAP_DMAC_OUT_NUM (RNPM_ETH_BASE + 0x82d8) +#define RNPM_ETH_DECAP_BMC_OUT_NUM (RNPM_ETH_BASE + 0x82dc) +#define RNPM_ETH_DECAP_SW_OUT_NUM (RNPM_ETH_BASE + 0x82e0) +#define RNPM_ETH_DECAP_MIRROR_OUT_NUM (RNPM_ETH_BASE + 0x82e4) +#define RNPM_ETH_DECAP_PKT_DROP_NUM(port) \ + (RNPM_ETH_BASE + 0x82e8 + 0x04 * (port)) +#define RNPM_ETH_INVALID_DROP_PKTS RNPM_ETH_DECAP_PKT_DROP_NUM(0) +#define RNPM_ETH_FILTER_DROP_PKTS RNPM_ETH_DECAP_PKT_DROP_NUM(1) +#define RNPM_ETH_DECAP_DMAC_DROP_NUM (RNPM_ETH_BASE + 0x82f0) +#define RNPM_ETH_DECAP_BMC_DROP_NUM (RNPM_ETH_BASE + 0x82f4) +#define RNPM_ETH_DECAP_SWITCH_DROP_NUM (RNPM_ETH_BASE + 0x82f8) +#define RNPM_ETH_DECAP_RM_VLAN_NUM (RNPM_ETH_BASE + 0x82fc) +#define RNPM_ETH_RX_FC_PKT_IN_NUM (RNPM_ETH_BASE + 0x8300) +#define RNPM_ETH_RX_FC_PKT_OUT_NUM (RNPM_ETH_BASE + 0x8304) +#define RNPM_ETH_RX_FC_PKT_DROP0_NUM (RNPM_ETH_BASE + 0x8308) +#define RNPM_ETH_RX_FC_PKT_DROP1_NUM (RNPM_ETH_BASE + 0x830c) +#define RNPM_ETH_RING_FC_STATUS0 (RNPM_ETH_BASE + 0x8310) +#define RNPM_ETH_RING_FC_STATUS1 (RNPM_ETH_BASE + 0x8314) +#define RNPM_ETH_RING_FC_STATUS2 (RNPM_ETH_BASE + 0x8318) +#define RNPM_ETH_RING_FC_STATUS3 (RNPM_ETH_BASE + 0x831c) +#define RNPM_ETH_RX_DEBUG(n) (RNPM_ETH_BASE + 0x8400 + 0x04 * (n)) +#define RNPM_ETH_RX_FC_DEBUG0_NUM RNPM_ETH_RX_DEBUG(0) +#define RNPM_ETH_RX_FC_DEBUG1_NUM RNPM_ETH_RX_DEBUG(1) +#define RNPM_ETH_RX_DIS_DEBUG0_NUM RNPM_ETH_RX_DEBUG(2) +#define RNPM_ETH_RX_DIS_DEBUG1_NUM RNPM_ETH_RX_DEBUG(3) +#define RNPM_ETH_HOST_L2_DROP_PKTS RNPM_ETH_RX_DEBUG(4) +#define RNPM_ETH_REDIR_INPUT_MATCH_DROP_PKTS RNPM_ETH_RX_DEBUG(5) +#define RNPM_ETH_ETYPE_DROP_PKTS RNPM_ETH_RX_DEBUG(6) +#define RNPM_ETH_TCP_SYN_DROP_PKTS RNPM_ETH_RX_DEBUG(7) +#define RNPM_ETH_REDIR_TUPLE5_DROP_PKTS RNPM_ETH_RX_DEBUG(8) +#define RNPM_ETH_REDIR_TCAM_DROP_PKTS RNPM_ETH_RX_DEBUG(9) +#define RNPM_ETH_VMARK_TC(n) (RNPM_ETH_BASE + 0x8500 + 0x04 * (n)) +#define RNPM_RING_FC_ENABLE (RNPM_ETH_BASE + 0x8520) +#define RNPM_SELECT_RING_EN(n) (RNPM_ETH_BASE + 0x8524 + (0x4 * n)) +#define RNPM_TC_FC_SW_EN (RNPM_ETH_BASE + 0x8534) + +#define RNPM_ETH_LOCAL_DIP(n) (RNPM_ETH_BASE + 0x8600 + 0x04 * (n)) +#define RNPM_ETH_LOCAL_DMAC_H(n) (RNPM_ETH_BASE + 0x8700 + 0x04 * (n)) +#define RNPM_ETH_LOCAL_DMAC_L(n) (RNPM_ETH_BASE + 0x8800 + 0x04 * (n)) +/* Rx Ring Flow Control */ +/* tc 8 */ +#define RNPM_RXTRANS_RX_PKTS(port) (RNPM_ETH_BASE + 0x8900 + 0x40 * (port)) +#define RNPM_RXTRANS_DROP_PKTS(port) (RNPM_ETH_BASE + 0x8904 + 0x40 * (port)) +#define RNPM_RXTRANS_WDT_ERR_PKTS(port) (RNPM_ETH_BASE + 0x8908 + 0x40 * (port)) +#define RNPM_RXTRANS_CODE_ERR_PKTS(port) \ + (RNPM_ETH_BASE + 0x890c + 0x40 * (port)) +#define RNPM_RXTRANS_CRC_ERR_PKTS(port) (RNPM_ETH_BASE + 0x8910 + 0x40 * (port)) +#define RNPM_RXTRANS_SLEN_ERR_PKTS(port) \ + (RNPM_ETH_BASE + 0x8914 + 0x40 * (port)) +#define RNPM_RXTRANS_GLEN_ERR_PKTS(port) \ + (RNPM_ETH_BASE + 0x8918 + 0x40 * (port)) +#define RNPM_RXTRANS_IPH_ERR_PKTS(port) (RNPM_ETH_BASE + 0x891c + 0x40 * (port)) +#define RNPM_RXTRANS_CSUM_ERR_PKTS(port) \ + (RNPM_ETH_BASE + 0x8920 + 0x40 * (port)) +#define RNPM_RXTRANS_LEN_ERR_PKTS(port) (RNPM_ETH_BASE + 0x8924 + 0x40 * (port)) +#define RNPM_RXTRANS_CUT_ERR_PKTS(port) (RNPM_ETH_BASE + 0x8928 + 0x40 * (port)) +#define RNPM_RXTRANS_EXCEPT_BYTES(port) (RNPM_ETH_BASE + 0x892c + 0x40 * (port)) +#define RNPM_RXTRANS_G1600_BYTES_PKTS(port) \ + (RNPM_ETH_BASE + 0x8930 + 0x40 * (port)) + +#define RNPM_RX_RING_MAXRATE(n) (RNPM_ETH_BASE + 0x8a00 + (0x4 * n)) +// emac_mng_filter no used in host +#define RNPM_ETH_RX_PROGFULL_RTRN(n) (RNPM_ETH_BASE + 0x8c00 + 0x04 * (n)) +#define RNPM_ETH_CNT_PKT_EMAC_RX(n) (RNPM_ETH_BASE + 0x8c10 + 0x04 * (n)) +#define RNPM_ETH_CNT_PKT_PECL_RX(n) (RNPM_ETH_BASE + 0x8c20 + 0x04 * (n)) +#define RNPM_ETH_STATUS_RX_FLOWCTRL(n) (RNPM_ETH_BASE + 0x8c30 + 0x04 * (n)) + +#define RNPM_ETH_DMAC_FCTRL (RNPM_ETH_BASE + 0x9110) +#define RNPM_ETH_DMAC_MCSTCTRL (RNPM_ETH_BASE + 0x9114) +#define RNPM_MCSTCTRL_MULTICASE_TBL_EN (1 << 2) +#define RNPM_MCSTCTRL_UNICASE_TBL_EN (1 << 3) +#define RNPM_MCSTCTRL_DMAC_47 (0x00) +#define RNPM_MCSTCTRL_DMAC_46 (0x01) +#define RNPM_MCSTCTRL_DMAC_45 (0x02) +#define RNPM_MCSTCTRL_DMAC_43 (0x03) +#define RNPM_ETH_VLAN_FILTER_ENABLE (RNPM_ETH_BASE + 0x9118) + +#define RNPM_ETH_INPORT_POLICY_VAL (RNPM_ETH_BASE + 0x91d0) +#define RNPM_ETH_INPORT_POLICY_REG(n) (RNPM_ETH_BASE + 0x91e0 + 0x04 * (n)) +#define ETH_LAYER2_NUM (16) +#define RNPM_ETH_LAYER2_ETQF(n) (RNPM_ETH_BASE + 0x9200 + 0x04 * (n)) +#define RNPM_ETH_LAYER2_ETQS(n) (RNPM_ETH_BASE + 0x9240 + 0x04 * (n)) +#define RNPM_ETH_LAYER2_ETQS_DEFAULT (RNPM_ETH_BASE + 0x9280) +#define RNPM_ETH_ETQF_DEFAULT (RNPM_ETH_BASE + 0x9284) +#define RNPM_ETH_SYNQF (RNPM_ETH_BASE + 0x9290) +#define RNPM_ETH_SYNQF_PRIORITY (RNPM_ETH_BASE + 0x9294) +/* [3:0]: + * 4'b0000: RSS disable + * 4'b0001: RSS only + * 4'b0100: DCB and RSS--8 * 16 + * 4'b1010: POOLS and RSS--32 * 4 + * [3] :virtual enable + * [16]:ipv4_hash_tcp_enable + * [17]:ipv4_hash_enable + * [20]:ipv6_hash_enable + * [21]:ipv6_hash_tcp_enable + * [22]:ipv4_hash_udp_enable + * [23]:ipv6_hash_udp_enable + * [24]:ipv4_hash_sctp_enable + * [25]:ipv6_hash_sctp_enable + */ +#define RNPM_ETH_RSS_CONTROL (RNPM_ETH_BASE + 0x92a0) +#define RNPM_MRQC_IOV_EN (RNPM_ETH_BASE + 0x92a0) +#define RNPM_IOV_ENABLED (1 << 3) +#define RNPM_ETH_RSS_KEY (RNPM_ETH_BASE + 0x92d0) + +#define RNPM_ETH_RAR_RL(n) (RNPM_ETH_BASE + 0xa000 + 0x04 * n) +#define RNPM_ETH_RAR_RH(n) (RNPM_ETH_BASE + 0xa400 + 0x04 * n) +#define RNPM_ETH_UTA(n) (RNPM_ETH_BASE + 0xa800 + 0x04 * n) +#define RNPM_ETH_MUTICAST_HASH_TABLE(n) (RNPM_ETH_BASE + 0xac00 + 0x04 * n) +#define RNPM_MTA(n) RNPM_ETH_MUTICAST_HASH_TABLE(n) + +#define RNPM_ETH_VLAN_FILTER_TABLE(n) (RNPM_ETH_BASE + 0xb000 + 0x04 * (n)) +#define RNPM_VFTA RNPM_ETH_VLAN_FILTER_TABLE +#define RNPM_FCTRL_MULTICASE_BYPASS (1 << 8) +#define RNPM_FCTRL_UNICASE_BYPASS (1 << 9) +#define RNPM_FCTRL_BROADCASE_BYPASS (1 << 10) + +#define RNPM_ETH_ETYPE_TABLE(n) (RNPM_ETH_BASE + 0xb300 + 0x04 * (n)) +#define RNPM_VM_DMAC_MPSAR_RING(entry) (RNPM_ETH_BASE + 0xb400 + (4 * (entry))) +#define RNPM_VLVF(idx) (RNPM_ETH_BASE + 0xb600 + 4 * (idx)) +#define RNPM_VLVFB(idx) (RNPM_ETH_BASE + 0xb700 + 4 * (idx)) +#define RNPM_VM_TUNNEL_PFVLVF_L(n) (RNPM_ETH_BASE + 0xb800 + 0x04 * (n)) +#define RNPM_VM_TUNNEL_PFVLVF_H(n) (RNPM_ETH_BASE + 0xb900 + 0x04 * (n)) +/* 5 tuple */ +#define ETH_TUPLE5_NUM (128) +#define RNPM_ETH_TUPLE5_SAQF(n) (RNPM_ETH_BASE + 0xc000 + 0x04 * (n)) +#define RNPM_ETH_TUPLE5_DAQF(n) (RNPM_ETH_BASE + 0xc400 + 0x04 * (n)) +#define RNPM_ETH_TUPLE5_SDPQF(n) (RNPM_ETH_BASE + 0xc800 + 0x04 * (n)) +#define RNPM_ETH_TUPLE5_FTQF(n) (RNPM_ETH_BASE + 0xcc00 + 0x04 * (n)) +#define RNPM_ETH_TUPLE5_POLICY(n) (RNPM_ETH_BASE + 0xd000 + 0x04 * (n)) +#define RNPM_ETH_RSS_INDIR_TBL(p, n) \ + (RNPM_ETH_BASE + 0xe000 + 0x04 * (n) + 0x200 * (p)) +/* tc is 8 */ +#define RNPM_ETH_TC_IPH_OFFSET_TABLE(n) (RNPM_ETH_BASE + 0xe800 + 0x04 * (n)) +#define RNPM_ETH_TC_VLAN_OFFSET_TABLE(n) (RNPM_ETH_BASE + 0xe820 + 0x04 * (n)) +/* port is 4 */ +#define RNPM_ETH_TC_PORT_OFFSET_TABLE(n) (RNPM_ETH_BASE + 0xe840 + 0x04 * (n)) +#define RNPM_REDIR_RING_MASK (RNPM_ETH_BASE + 0xe860) +/* uv3p only */ +#define RNPM_ETH_RSS_MODE (0x6fe00) +#define RNPM_ETH_RSS_INDIR_TBL_UV3P(n) (0x6ff00 + 0x04 * (n)) + +/* ================================================================== */ + +/* ==================== RNPM-REG Global Registers ==================== */ +#define RNPM_COMM_REG0 (0x30000) +#define RNPM_TOP_NIC_VERSION (RNPM_COMM_REG0 + 0x0000) +#define RNPM_TOP_NIC_CONFIG (RNPM_COMM_REG0 + 0x0004) +#define RNPM_TOP_NIC_STAT (RNPM_COMM_REG0 + 0x0008) +#define RNPM_TOP_NIC_DUMMY (RNPM_COMM_REG0 + 0x000c) +#define RNPM_TOP_NIC_REST_N (RNPM_COMM_REG0 + 0x0010) +#define NIC_RESET (0) +/* dma top */ +#define RNPM_TOP_DMA_MEM_SLP (RNPM_COMM_REG0 + 0x4004) +#define RNPM_TOP_DMA_MEM_SD (RNPM_COMM_REG0 + 0x4008) +/* eth top */ +#define RNPM_TOP_ETH_TIMESTAMP_SEL (RNPM_COMM_REG0 + 0x8010) +#define RNPM_TOP_ETH_MAC_CLK_SEL (RNPM_COMM_REG0 + 0x8014) +#define RNPM_TOP_ETH_INF_ETH_STATUS (RNPM_COMM_REG0 + 0x8018) +#define RNPM_TOP_ETH_BUG_40G_PATCH (RNPM_COMM_REG0 + 0x801c) +#define RNPM_TOP_ETH_PWR_PORT_NUM (4) +#define RNPM_TOP_ETH_PWR_CLAMP_CTRL_PORT(n) \ + (RNPM_COMM_REG0 + 0x8020 + 0xc * (n)) +#define RNPM_TOP_ETH_PWR_ISOLATE_PORT(n) (RNPM_COMM_REG0 + 0x8024 + 0xc * (n)) +#define RNPM_TOP_ETH_PWR_DOWN_PORT(n) (RNPM_COMM_REG0 + 0x8028 + 0xc * (n)) +#define RNPM_TOP_ETH_TCAM_CONFIG_ENABLE (RNPM_COMM_REG0 + 0x8050) +#define RNPM_TOP_ETH_SLIP (RNPM_COMM_REG0 + 0x8060) +#define RNPM_TOP_ETH_SHUT_DOWN (RNPM_COMM_REG0 + 0x8064) +#define RNPM_TOP_ETH_OVS_SLIP (RNPM_COMM_REG0 + 0x8068) +#define RNPM_TOP_ETH_OVS_SHUT_DOWN (RNPM_COMM_REG0 + 0x806c) +/* ?? */ +#define RNPM_FC_PORT_ENABLE (RNPM_COMM_REG0 + 0x9004) +#define RNPM_FC_PORT_PRIO_MAP(n) (RNPM_COMM_REG0 + 0x9008 + (0x04 * n)) +#define RNPM_FC_EN_CONF_AVAILBLE (RNPM_COMM_REG0 + 0x9018) +#define RNPM_FC_UNCTAGS_MAP_OFFSET (16) +/* mac top */ +#define RNPM_TOP_MAC_OUI (RNPM_COMM_REG0 + 0xc004) +#define RNPM_TOP_MAC_SN (RNPM_COMM_REG0 + 0xc008) +/* ================================================================== */ + +/* ==================== RNPM-SERDES Global Registers ================= */ + +#define RNPM_SERDES (0x40000) + +#define RNPM_PCS_OFFSET (0x1000) + +#define RNPM_PCS_BASE(i) (RNPM_SERDES + RNPM_PCS_OFFSET * i) +#define RNPM_PCS_1G_OR_10G BIT(13) +#define RNPM_PCS_SPPEED_MASK (0x1c) +#define RNPM_PCS_SPPEED_10G (0x0) +#define RNPM_PCS_SPPEED_40G (0xc) +#define RNPM_PCS_LINK_SPEED (0x30000) +#define RNPM_PCS_LINKUP BIT(2) +#define RNPM_PCS_LINK_STATUS (0x30001) + +/* ================================================================== */ + +/* ==================== RNPM-MAC Global Registers ==================== */ +/* === MAC Registers== */ +#define RNPM_XLMAC (0x60000) + +#define MAC_OFFSET (0x10000) + +#define RNPM_MAC_TX_CFG(i) (RNPM_XLMAC + 0x0000 + i * MAC_OFFSET) + +#define RNPM_MAC_RX_CFG(i) (RNPM_XLMAC + 0x0004 + i * MAC_OFFSET) +#define RNPM_RX_ALL BIT(0) +#define RNPM_RX_ALL_MUL BIT(4) +#define RNPM_RX_HUC BIT(1) +#define RNPM_VLAN_HASH_EN BIT(16) +#define RNPM_RA BIT(31) +#define RNPM_MAX_RX_CFG_IPC BIT(9) +#define RNPM_HPF BIT(10) +#define RNPM_MAC_PKT_FLT(i) (RNPM_XLMAC + 0x0008 + i * MAC_OFFSET) +#define RNPM_FLT_HMC BIT(2) +#define RNPM_FLT_HUC BIT(1) +#define RNPM_MAC_MC_HASH_TABLE(i, idx) \ + (RNPM_XLMAC + 0x0010 + 0x04 * idx + i * MAC_OFFSET) +#define RNPM_MAC_LPI_CTRL(i) (RNPM_XLMAC + 0x00d0 + i * MAC_OFFSET) + +#define RNPM_ERIVLT BIT(27) +#define RNPM_EDVLP BIT(26) +#define RNPM_VTHM BIT(25) +#define RNPM_EVLRXS BIT(24) +#define RNPM_EVLS_OFFSET (21) +#define RNPM_EVLS_ALWAYS_STRIP (0x3) +#define RNPM_DOVLTC BIT(20) +#define RNPM_ERSVLM BIT(19) +#define RNPM_ESVL BIT(18) +#define RNPM_VTIM BIT(17) +#define RNPM_ETV BIT(16) +#define RNPM_VL_MODE_ON (0xFFFF) +#define RNPM_VL_MODE_OFF (0x0000) +#define RNPM_MAC_TX_VLAN_TAG(i) (RNPM_XLMAC + 0x0050 + i * MAC_OFFSET) +#define RNPM_VLTI BIT(20) +#define RNPM_CSVL BIT(19) +#define RNPM_MAC_TX_VLAN_MODE(i) (RNPM_XLMAC + 0x0060 + i * MAC_OFFSET) +#define RNPM_MAC_INNER_VLAN_INCL(i) (RNPM_XLMAC + 0x0064 + i * MAC_OFFSET) +#define RNPM_MAC_VLAN_HASH_TB(i) (RNPM_XLMAC + 0x0058 + i * MAC_OFFSET) + +#define RNPM_MAC_Q0_TX_FLOW_CTRL(i, num) \ + (RNPM_XLMAC + 0x0070 + i * MAC_OFFSET + 0x04 * (num)) +#define RNPM_MAC_RX_FLOW_CTRL(i) (RNPM_XLMAC + 0x0090 + i * MAC_OFFSET) +#define RNPM_MAC_HW_FEATURE(i) (RNPM_XLMAC + 0x0120 + i * MAC_OFFSET) + +#define RNPM_MAC_UNICAST_LOW(i, port) \ + (RNPM_XLMAC + 0x304 + i * 0x08 + port * MAC_OFFSET) +#define RNPM_MAC_UNICAST_HIGH(i, port) \ + (RNPM_XLMAC + 0x300 + i * 0x08 + port * MAC_OFFSET) +/* 1588 */ +#define RNPM_MAC_TS_CTRL(i) (RNPM_XLMAC + 0X0d00 + i * MAC_OFFSET) +#define RNPM_MAC_SUB_SECOND_INCREMENT(i) (RNPM_XLMAC + 0x0d04 + i * MAC_OFFSET) +#define RNPM_MAC_SYS_TIME_SEC_CFG(i) (RNPM_XLMAC + 0x0d08 + i * MAC_OFFSET) +#define RNPM_MAC_SYS_TIME_NANOSEC_CFG(i) (RNPM_XLMAC + 0x0d0c + i * MAC_OFFSET) +#define RNPM_MAC_SYS_TIME_SEC_UPDATE(i) (RNPM_XLMAC + 0x0d10 + i * MAC_OFFSET) +#define RNPM_MAC_SYS_TIME_NANOSEC_UPDATE(i) \ + (RNPM_XLMAC + 0x0d14 + i * MAC_OFFSET) +#define RNPM_MAC_TS_ADDEND(i) (RNPM_XLMAC + 0x0d18 + i * MAC_OFFSET) +#define RNPM_MAC_TS_STATS(i) (RNPM_XLMAC + 0x0d20 + i * MAC_OFFSET) +#define RNPM_MAC_INTERRUPT_ENABLE(i) (RNPM_XLMAC + 0x00b4 + i * MAC_OFFSET) + +#define RNPM_MAC_STATS_BROADCAST_LOW(i) (RNPM_XLMAC + 0x0918 + i * MAC_OFFSET) +#define RNPM_MAC_STATS_BROADCAST_HIGH(i) (RNPM_XLMAC + 0x091c + i * MAC_OFFSET) +#define RNPM_MAC_STATS_MULTICAST_LOW(i) (RNPM_XLMAC + 0x0920 + i * MAC_OFFSET) +#define RNPM_MAC_STATS_MULTICAST_HIGH(i) (RNPM_XLMAC + 0x0924 + i * MAC_OFFSET) + +#define RNPM_MAC_STATS_RX_PAUSE_LOW(i) (RNPM_XLMAC + 0x0988 + i * MAC_OFFSET) +#define RNPM_MAC_STATS_RX_PAUSE_HIGH(i) (RNPM_XLMAC + 0x098c + i * MAC_OFFSET) + +#define RNPM_MAC_STATS_TX_PAUSE_LOW(i) (RNPM_XLMAC + 0x0894 + i * MAC_OFFSET) +#define RNPM_MAC_STATS_TX_PAUSE_HIGH(i) (RNPM_XLMAC + 0x0898 + i * MAC_OFFSET) + +#define RNPM_TX_FLOW_ENABLE_MASK (0x2) +#define RNPM_RX_FLOW_ENABLE_MASK (0x1) +/* ================================================================== */ + +/* ==================== RNPM-MSIX Global Registers ==================== */ +//==== Ring-MSIX Registers (MSI-X_module_design.docs) === +#define RING_VECTOR(n) (0x04 * (n)) + +/* ================================================================== */ + +/* ==================== RNPM-SWITCH Global Registers ================= */ +#define RNPM_SWITCH_BASE (0xB0000) + +/* port is 6 */ +#define RNPM_SWITCH_RULE_INGS(port, n) \ + (RNPM_SWITCH_BASE + 0x24 * (port) + 0x1000 + 0x04 * (n)) +#define RNPM_SWITCH_RULE_INGS_RPU_NP(port) \ + (RNPM_SWITCH_BASE + 0x24 * (port) + 0x1014) +#define RNPM_SWITCH_RULE_INGS_RPU_SWITCH(port) \ + (RNPM_SWITCH_BASE + 0x24 * (port) + 0x1018) +#define RNPM_SWITCH_RULE_INGS_SEC(port) \ + (RNPM_SWITCH_BASE + 0x24 * (port) + 0x101c) +#define RNPM_SWITCH_RULE_INGS_EXFPGA(port) \ + (RNPM_SWITCH_BASE + 0x24 * (port) + 0x1020) +#define RNPM_SWITCH_CNT_EGRESS_PKT(port) \ + (RNPM_SWITCH_BASE + 0x10db + 0x04 * (n)) +#define RNPM_SWITCH_CNT_INGRESS_PKT(port) \ + (RNPM_SWITCH_BASE + 0x10f0 + 0x04 * (n)) +#define RNPM_SWITCH_RPUUP_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x1108) +#define RNPM_SWITCH_RPUDN_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x110c) +#define RNPM_SWITCH_MAC0_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x1110) +#define RNPM_SWITCH_MAC1_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x1114) +#define RNPM_SWITCH_DMA0_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x1118) +#define RNPM_SWITCH_DMA1_DATA_PROG_FULL_THRESH (RNPM_SWITCH_BASE + 0x111c) +#define RNPM_SWITCH_REG1_INGRESS_STATUS(port) \ + (RNPM_SWITCH_BASE + 0x1120 + 0x08 * (port)) +#define RNPM_SWITCH_REG2_INGRESS_STATUS(port) \ + (RNPM_SWITCH_BASE + 0x1124 + 0x08 * (port)) +#define RNPM_SWITCH_REG_STATUS_ROBIN(port) \ + (RNPM_SWITCH_BASE + 0x1150 + 0x04 * (port)) +#define RNPM_SWITCH_REG_EGRESS_STATUS(port) \ + (RNPM_SWITCH_BASE + 0x1168 + 0x04 * (port)) +#define RNPM_SWITCH_INFO_FIFO_DMA_TX(n) (RNPM_SWITCH_BASE + 0x1198 + 0x08 * (n)) +#define RNPM_SWITCH_INFO_FIFO_DMA_RX(n) (RNPM_SWITCH_BASE + 0x119c + 0x08 * (n)) +#define RNPM_SWITCH_INFO_FIFO_MAC_TX(n) (RNPM_SWITCH_BASE + 0x11a8 + 0x08 * (n)) +#define RNPM_SWITCH_INFO_FIFO_MAC_RX(n) (RNPM_SWITCH_BASE + 0x11ac + 0x08 * (n)) +#define RNPM_SWITCH_INFO_FIFO_RPUUP_RX(n) \ + (RNPM_SWITCH_BASE + 0x11bc + 0x08 * (n)) +#define RNPM_SWITCH_INFO_FIFO_RPUDN_RX(n) \ + (RNPM_SWITCH_BASE + 0x11c0 + 0x08 * (n)) +#define RNPM_SWITCH_EN_SOFT_RESET (RNPM_SWITCH_BASE + 0xf000) +#define RNPM_SWITCH_SOFT_RESET (RNPM_SWITCH_BASE + 0xf004) +#define RNPM_SWITCH_CLR_INGS_ERR (RNPM_SWITCH_BASE + 0xf008) +#define RNPM_SWITCH_ERR_CODE_INGS(port) \ + (RNPM_SWITCH_BASE + 0xf010 + 0x04 * (port)) +#define RNPM_SWITCH_MEM_SD (RNPM_SWITCH_BASE + 0xf028) +#define RNPM_SWITCH_MEM_SLP (RNPM_SWITCH_BASE + 0xf02c) +#define RNPM_SWITCH_EN_INVALID_DPORT_DROP_O (RNPM_SWITCH_BASE + 0xf030) + +/* ================================================================== */ + +/* ==================== RNPM-TCAM Global Registers ==================== */ +#define RNPM_TCAM_BASE (0xc0000) + +#define RNPM_TCAM_SDPQF(n) \ + (RNPM_TCAM_BASE + 0x00 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_DAQF(n) \ + (RNPM_TCAM_BASE + 0x04 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_SAQF(n) \ + (RNPM_TCAM_BASE + 0x08 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_APQF(n) \ + (RNPM_TCAM_BASE + 0x0c + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_SDPQF_MASK(n) \ + (RNPM_TCAM_BASE + 0x20 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_DAQF_MASK(n) \ + (RNPM_TCAM_BASE + 0x24 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_SAQF_MASK(n) \ + (RNPM_TCAM_BASE + 0x28 + 0x40 * (n / 2) + 0x10 * (n % 2)) +#define RNPM_TCAM_APQF_MASK(n) \ + (RNPM_TCAM_BASE + 0x2c + 0x40 * (n / 2) + 0x10 * (n % 2)) + +#define RNPM_TCAM_MODE (RNPM_TCAM_BASE + 0x20000) +#define RNPM_TCAM_CACHE_ENABLE (RNPM_TCAM_BASE + 0x20004) +#define RNPM_TCAM_CACHE_ADDR_CLR (RNPM_TCAM_BASE + 0x20008) +#define RNPM_TCAM_CACHE_REQ_CLR (RNPM_TCAM_BASE + 0x2000c) + +/* ================================================================== */ + +/* ==================== OTHER Global Registers ==================== */ +/* ===== PF-VF Functions ==== */ +#define VF_NUM_REG (0xa3000) +/* 8bit: 7:vf_actiove 6:fun0/fun1 [5:0]:vf_num */ +#define VF_NUM(vfnum, fun) ((1 << 7) | (((fun)&0x1) << 6) | ((vfnum)&0x3F)) +#define PF_NUM(fun) (((fun)&0x1) << 6) + +#define IS_VF(vfnum) (((vfnum) & (1 << 7)) ? 1 : 0) + +/* PFC Flow Control*/ +enum NIC_MODE { + MODE_NIC_MODE_1PORT_40G = 0, + MODE_NIC_MODE_1PORT = 1, + MODE_NIC_MODE_2PORT = 2, + MODE_NIC_MODE_4PORT = 3, +}; + +/* ================================================================== */ +#endif /* RNPM_REGS_H */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.c b/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.c new file mode 100644 index 000000000000..d7b97d970c23 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.c @@ -0,0 +1,1088 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef NETIF_F_HW_VLAN_CTAG_TX +#include +#endif + +#include "rnpm.h" +#include "rnpm_type.h" +#include "rnpm_sriov.h" + +#ifdef CONFIG_PCI_IOV +static int __rnpm_enable_sriov(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + int num_vf_macvlans, i; + struct vf_macvlans *mv_list; + u32 v; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + adapter->flags |= RNPM_FLAG_SRIOV_ENABLED; + e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + + /* Enable VMDq flag so device will be set in VM mode */ + adapter->flags |= RNPM_FLAG_VMDQ_ENABLED; + if (!adapter->ring_feature[RING_F_VMDQ].limit) + adapter->ring_feature[RING_F_VMDQ].limit = 1; + adapter->ring_feature[RING_F_VMDQ].offset = adapter->num_vfs; + + num_vf_macvlans = hw->mac.num_rar_entries - + (RNPM_MAX_PF_MACVLANS + 1 + adapter->num_vfs); + + adapter->mv_list = mv_list = kcalloc( + num_vf_macvlans, sizeof(struct vf_macvlans), GFP_KERNEL); + if (mv_list) { + /* Initialize list of VF macvlans */ + INIT_LIST_HEAD(&adapter->vf_mvs.l); + for (i = 0; i < num_vf_macvlans; i++) { + mv_list->vf = -1; + mv_list->free = true; + mv_list->rar_entry = hw->mac.num_rar_entries - + (i + adapter->num_vfs + 1); + list_add(&mv_list->l, &adapter->vf_mvs.l); + mv_list++; + } + } + + /* Initialize default switching mode VEB */ + wr32(hw, RNPM_DMA_CONFIG, + rd32(hw, RNPM_DMA_CONFIG) & (~DMA_VEB_BYPASS)); + adapter->flags2 |= RNPM_FLAG2_BRIDGE_MODE_VEB; + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + + //ETH_BYPASS + rd32(hw, RNPM_ETH_BYPASS); + + wr32(hw, RNPM_HOST_FILTER_EN, 1); + wr32(hw, RNPM_REDIR_EN, 1); + v = rd32(hw, RNPM_MRQC_IOV_EN); + v |= RNPM_IOV_ENABLED; + wr32(hw, RNPM_MRQC_IOV_EN, v); + + wr32(hw, RNPM_ETH_DMAC_FCTRL, + rd32(hw, RNPM_ETH_DMAC_FCTRL) | RNPM_FCTRL_BROADCASE_BYPASS); + //wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, v); + + /* If call to enable VFs succeeded then allocate memory + * for per VF control structures. + */ + adapter->vfinfo = kcalloc(adapter->num_vfs, + sizeof(struct vf_data_storage), GFP_KERNEL); + if (adapter->vfinfo) { + /* limit trafffic classes based on VFs enabled */ + /* TODO analyze VF need support pfc or traffic classes */ + /* We do not support RSS w/ SR-IOV */ + adapter->ring_feature[RING_F_RSS].limit = 2; + + /* Disable RSC when in SR-IOV mode */ + adapter->flags2 &= + ~(RNPM_FLAG2_RSC_CAPABLE | RNPM_FLAG2_RSC_ENABLED); + + /* enable spoof checking for all VFs */ + //for (i = 0; i < adapter->num_vfs; i++) + //adapter->vfinfo[i].spoofchk_enabled = true; + return 0; + } + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + return -ENOMEM; +} + +/* Note this function is called when the user wants to enable SR-IOV + * VFs using the now deprecated module parameter + */ +void rnpm_enable_sriov(struct rnpm_adapter *adapter) +{ + int pre_existing_vfs = 0; + + pre_existing_vfs = pci_num_vf(adapter->pdev); + if (!pre_existing_vfs && !adapter->num_vfs) + return; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + if (!pre_existing_vfs) + dev_warn( + &adapter->pdev->dev, + "Enabling SR-IOV VFs using the module parameter is deprecated - please use the pci sysfs interface.\n"); + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + /* If there are pre-existing VFs then we have to force + * use of that many - over ride any module parameter value. + * This may result from the user unloading the PF driver + * while VFs were assigned to guest VMs or because the VFs + * have been created via the new PCI SR-IOV sysfs interface. + */ + if (pre_existing_vfs) { + adapter->num_vfs = pre_existing_vfs; + dev_warn( + &adapter->pdev->dev, + "Virtual Functions already enabled for this device - Please reload all VF drivers to avoid spoofed packet errors\n"); + } else { + int err; + /* The n10 supports up to 64 VFs per physical function + * but this implementation limits allocation to 127 so that + * basic networking resources are still available to the + * physical function. If the user requests greater than + * 64 VFs then it is an error - reset to default of zero. + */ + adapter->num_vfs = min_t(unsigned int, adapter->num_vfs, + RNPM_MAX_VF_CNT - 1); + + err = pci_enable_sriov(adapter->pdev, adapter->num_vfs); + if (err) { + e_err(probe, "Failed to enable PCI sriov: %d\n", err); + adapter->num_vfs = 0; + return; + } + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + } + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + if (!__rnpm_enable_sriov(adapter)) + return; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + /* If we have gotten to this point then there is no memory available + * to manage the VF devices - print message and bail. + */ + e_err(probe, "Unable to allocate memory for VF Data Storage\n"); + rnpm_disable_sriov(adapter); +} + +static bool rnpm_vfs_are_assigned(struct rnpm_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct pci_dev *vfdev; + int dev_id; + unsigned int vendor_id; + + if (adapter->pdev->device == RNPM_DEV_ID_N10_PF0) { + vendor_id = 0x1dab; + dev_id = RNPM_DEV_ID_N10_PF0_VF; + } else if (adapter->pdev->device == RNPM_DEV_ID_N10_PF1) { + vendor_id = 0x1dab; + dev_id = RNPM_DEV_ID_N10_PF1_VF; + } else if (adapter->pdev->device == RNPM_DEV_ID_N10_PF0_N) { + vendor_id = PCI_VENDOR_ID_MUCSE; + dev_id = RNPM_DEV_ID_N10_PF0_VF_N; + } else { + vendor_id = PCI_VENDOR_ID_MUCSE; + dev_id = RNPM_DEV_ID_N10_PF1_VF_N; + } + + /* loop through all the VFs to see if we own any that are assigned */ + vfdev = pci_get_device(vendor_id, dev_id, NULL); + while (vfdev) { + /* if we don't own it we don't care */ + if (vfdev->is_virtfn && vfdev->physfn == pdev) { + /* if it is assigned we cannot release it */ + if (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) + return true; + } + + vfdev = pci_get_device(vendor_id, dev_id, vfdev); + } + + return false; +} + +#endif /* #ifdef CONFIG_PCI_IOV */ +int rnpm_disable_sriov(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 v; + int rss; + + // disable + v = rd32(hw, RNPM_MRQC_IOV_EN); + v &= ~(RNPM_IOV_ENABLED); + wr32(hw, RNPM_MRQC_IOV_EN, v); + + /* set num VFs to 0 to prevent access to vfinfo */ + adapter->num_vfs = 0; + + /* free VF control structures */ + kfree(adapter->vfinfo); + adapter->vfinfo = NULL; + + /* free macvlan list */ + kfree(adapter->mv_list); + adapter->mv_list = NULL; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + /* if SR-IOV is already disabled then there is nothing to do */ + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return 0; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); +#ifdef CONFIG_PCI_IOV + /* If our VFs are assigned we cannot shut down SR-IOV + * without causing issues, so just leave the hardware + * available but disabled + */ + if (rnpm_vfs_are_assigned(adapter)) { + e_dev_warn( + "Unloading driver while VFs are assigned - VFs will not be deallocated\n"); + return -EPERM; + } + /* disable iov and allow time for transactions to clear */ + pci_disable_sriov(adapter->pdev); +#endif + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + + /* set default pool back to 0 */ + + /* Disable VMDq flag so device will be set in VM mode */ + if (adapter->ring_feature[RING_F_VMDQ].limit == 1) + adapter->flags &= ~RNPM_FLAG_VMDQ_ENABLED; + adapter->ring_feature[RING_F_VMDQ].offset = 0; + + rss = min_t(int, adapter->max_ring_pair_counts, num_online_cpus()); + adapter->ring_feature[RING_F_RSS].limit = rss; + + /* take a breather then clean up driver data */ + msleep(100); + adapter->flags &= ~RNPM_FLAG_SRIOV_ENABLED; + + dbg("%s:%d flags:0x%x\n", __func__, __LINE__, adapter->flags); + return 0; +} + +static int rnpm_pci_sriov_enable(struct pci_dev *dev, int num_vfs) +{ +#ifdef CONFIG_PCI_IOV + // todo fix me + struct rnpm_adapter *adapter = pci_get_drvdata(dev); + //struct rnpm_pf_adapter *pf_adapter = pci_get_drvdata(pdev); + int err = 0; + int i; + int pre_existing_vfs = pci_num_vf(dev); + + if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) { + err = -EACCES; + goto err_out; + } + + if (pre_existing_vfs && pre_existing_vfs != num_vfs) + err = rnpm_disable_sriov(adapter); + else if (pre_existing_vfs && pre_existing_vfs == num_vfs) + goto out; + + if (err) + goto err_out; + + /* While the SR-IOV capability structure reports total VFs to be + * 64 we limit the actual number that can be allocated to 63 so + * that some transmit/receive resources can be reserved to the + * PF. The PCI bus driver already checks for other values out of + * range. + */ + if (num_vfs > (RNPM_MAX_VF_FUNCTIONS - 1)) { + err = -EPERM; + goto err_out; + } + + adapter->num_vfs = num_vfs; + + err = __rnpm_enable_sriov(adapter); + if (err) + goto err_out; + + for (i = 0; i < adapter->num_vfs; i++) + rnpm_vf_configuration(dev, (i | 0x10000000)); + + err = pci_enable_sriov(dev, num_vfs); + if (err) { + e_dev_warn("Failed to enable PCI sriov: %d\n", err); + goto err_out; + } + dbg("flags:0x%x\n", adapter->flags); + rnpm_sriov_reinit(adapter); + +out: + return num_vfs; + +err_out: + return err; +#endif + return 0; +} + +static int rnpm_pci_sriov_disable(struct pci_dev *dev) +{ + struct rnpm_adapter *adapter = pci_get_drvdata(dev); + int err; + u32 current_flags = adapter->flags; + + err = rnpm_disable_sriov(adapter); + + /* Only reinit if no error and state changed */ + if (!err && current_flags != adapter->flags) { + /* rnpm_disable_sriov() doesn't clear VMDQ flag */ + adapter->flags &= ~RNPM_FLAG_VMDQ_ENABLED; +#ifdef CONFIG_PCI_IOV + rnpm_sriov_reinit(adapter); +#endif + } + + return err; +} + +static int rnpm_set_vf_multicasts(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + int entries = + (msgbuf[0] & RNPM_VT_MSGINFO_MASK) >> RNPM_VT_MSGINFO_SHIFT; + u16 *hash_list = (u16 *)&msgbuf[1]; + struct vf_data_storage *vfinfo = &adapter->vfinfo[vf]; + struct rnpm_hw *hw = &adapter->hw; + int i; + u32 vector_bit; + u32 vector_reg; + u32 mta_reg; + + /* only so many hash values supported */ + entries = min(entries, RNPM_MAX_VF_MC_ENTRIES); + + // enable multicast and unicast filter + mta_reg = rd32(hw, RNPM_ETH_DMAC_MCSTCTRL); + wr32(hw, RNPM_ETH_DMAC_MCSTCTRL, + mta_reg | RNPM_MCSTCTRL_MULTICASE_TBL_EN | + RNPM_MCSTCTRL_UNICASE_TBL_EN); + + /* salt away the number of multi cast addresses assigned + * to this VF for later use to restore when the PF multi cast + * list changes + */ + vfinfo->num_vf_mc_hashes = entries; + + /* VFs are limited to using the MTA hash table for their multicast + * addresses + */ + for (i = 0; i < entries; i++) + vfinfo->vf_mc_hashes[i] = hash_list[i]; + for (i = 0; i < vfinfo->num_vf_mc_hashes; i++) { + vector_reg = (vfinfo->vf_mc_hashes[i] >> 5) & 0x7F; + vector_bit = vfinfo->vf_mc_hashes[i] & 0x1F; + mta_reg = rd32(hw, RNPM_ETH_MUTICAST_HASH_TABLE(vector_reg)); + mta_reg |= (1 << vector_bit); + wr32(hw, RNPM_ETH_MUTICAST_HASH_TABLE(vector_reg), mta_reg); + } + + return 0; +} + +static void rnpm_restore_vf_macvlans(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct list_head *pos; + struct vf_macvlans *entry; + + list_for_each(pos, &adapter->vf_mvs.l) { + entry = list_entry(pos, struct vf_macvlans, l); + if (!entry->free) { + hw_dbg(hw, " vf:%d MACVLAN: RAR[%d] <= %pM\n", + entry->vf, entry->rar_entry, entry->vf_macvlan); + + hw->mac.ops.set_rar(hw, entry->rar_entry, + entry->vf_macvlan, entry->vf, + RNPM_RAH_AV); + } + } +} + +void rnpm_restore_vf_multicasts(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + struct vf_data_storage *vfinfo; + int i, j; + u32 vector_bit; + u32 vector_reg; + u32 mta_reg; + + hw_dbg(hw, "%s num_vf:%d\n", __func__, adapter->num_vfs); + + for (i = 0; i < adapter->num_vfs; i++) { + vfinfo = &adapter->vfinfo[i]; + for (j = 0; j < vfinfo->num_vf_mc_hashes; j++) { + hw->addr_ctrl.mta_in_use++; + vector_reg = (vfinfo->vf_mc_hashes[j] >> 5) & 0x7F; + vector_bit = vfinfo->vf_mc_hashes[j] & 0x1F; + mta_reg = rd32( + hw, RNPM_ETH_MUTICAST_HASH_TABLE(vector_reg)); + mta_reg |= (1 << vector_bit); + wr32(hw, RNPM_ETH_MUTICAST_HASH_TABLE(vector_reg), + mta_reg); + + hw_dbg(hw, " VF:%2d mc_hash:0x%x, MTA[%2d][%2d]=1\n", i, + vfinfo->vf_mc_hashes[j], vector_reg, vector_bit); + } + } + + /* Restore any VF macvlans */ + rnpm_restore_vf_macvlans(adapter); +} + +static int rnpm_set_vf_vlan(struct rnpm_adapter *adapter, int add, int vid, + u32 vf) +{ + /* VLAN 0 is a special case, don't allow it to be removed */ + if (!vid && !add) + return 0; + + return adapter->hw.mac.ops.set_vfta(&adapter->hw, vid, vf, (bool)add); +} + +static s32 rnpm_set_vf_lpe(struct rnpm_adapter *adapter, u32 *msgbuf, u32 vf) +{ + return 0; +} + +static void __maybe_unused rnpm_set_vmolr(struct rnpm_hw *hw, u32 vf, bool aupe) +{ +} + +static void __maybe_unused rnpm_clear_vmvir(struct rnpm_adapter *adapter, + u32 vf) +{ + // struct rnpm_hw *hw = &adapter->hw; + + //RNPM_WRITE_REG(hw, RNPM_VMVIR(vf), 0); +} +static inline void rnpm_vf_reset_event(struct rnpm_adapter *adapter, u32 vf) +{ + struct rnpm_hw *hw = &adapter->hw; + struct vf_data_storage *vfinfo = &adapter->vfinfo[vf]; + int rar_entry = hw->mac.num_rar_entries - (vf + 1); + u8 num_tcs = netdev_get_num_tc(adapter->netdev); + + /* add PF assigned VLAN or VLAN 0 */ + // rnpm_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf); + + /* reset offloads to defaults */ + // rnpm_set_vmolr(hw, vf, !vfinfo->pf_vlan); + + /* set outgoing tags for VFs */ + if (!vfinfo->pf_vlan && !vfinfo->pf_qos && !num_tcs) { + // rnpm_clear_vmvir(adapter, vf); + } else { + if (vfinfo->pf_qos || !num_tcs) + rnpm_set_vmvir(adapter, vfinfo->pf_vlan, vfinfo->pf_qos, + vf); + else + rnpm_set_vmvir(adapter, vfinfo->pf_vlan, + adapter->default_up, vf); + + //if (vfinfo->spoofchk_enabled) + // hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf); + } + + /* reset multicast table array for vf */ + adapter->vfinfo[vf].num_vf_mc_hashes = 0; + + /* Flush and reset the mta with the new values */ + rnpm_set_rx_mode(adapter->netdev); + + /* clear this rar_entry */ + hw->mac.ops.clear_rar(hw, rar_entry); + + /* reset VF api back to unknown */ + adapter->vfinfo[vf].vf_api = 0; +} + +static int rnpm_set_vf_mac(struct rnpm_adapter *adapter, int vf, + unsigned char *mac_addr) +{ + struct rnpm_hw *hw = &adapter->hw; + /* this rar_entry may be cofict with mac vlan with pf */ + int rar_entry = hw->mac.num_rar_entries - (vf + 1); + int vf_ring = vf * 2; + + memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, 6); + + hw->mac.ops.set_rar(hw, rar_entry, mac_addr, vf_ring / 2, RNPM_RAH_AV); + + return 0; +} + +static int rnpm_set_vf_macvlan(struct rnpm_adapter *adapter, int vf, int index, + unsigned char *mac_addr) +{ + struct rnpm_hw *hw = &adapter->hw; + struct list_head *pos; + struct vf_macvlans *entry; + + if (index <= 1) { + list_for_each(pos, &adapter->vf_mvs.l) { + entry = list_entry(pos, struct vf_macvlans, l); + if (entry->vf == vf) { + entry->vf = -1; + entry->free = true; + entry->is_macvlan = false; + hw->mac.ops.clear_rar(hw, entry->rar_entry); + } + } + } + + /* If index was zero then we were asked to clear the uc list + * for the VF. We're done. + */ + if (!index) + return 0; + + entry = NULL; + + list_for_each(pos, &adapter->vf_mvs.l) { + entry = list_entry(pos, struct vf_macvlans, l); + if (entry->free) + break; + } + + /* If we traversed the entire list and didn't find a free entry + * then we're out of space on the RAR table. Also entry may + * be NULL because the original memory allocation for the list + * failed, which is not fatal but does mean we can't support + * VF requests for MACVLAN because we couldn't allocate + * memory for the list management required. + */ + if (!entry || !entry->free) + return -ENOSPC; + + entry->free = false; + entry->is_macvlan = true; + entry->vf = vf; + memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN); + + hw->mac.ops.set_rar(hw, entry->rar_entry, mac_addr, vf, RNPM_RAH_AV); + return 0; +} + +int rnpm_vf_configuration(struct pci_dev *pdev, unsigned int event_mask) +{ + unsigned char vf_mac_addr[6]; + struct rnpm_adapter *adapter = pci_get_drvdata(pdev); + unsigned int vfn = (event_mask & 0x3f); + + bool enable = ((event_mask & 0x10000000U) != 0); + + if (enable) { + eth_zero_addr(vf_mac_addr); + memcpy(vf_mac_addr, adapter->hw.mac.perm_addr, 6); + vf_mac_addr[5] = vf_mac_addr[5] + (0x80 | vfn); + + memcpy(adapter->vfinfo[vfn].vf_mac_addresses, vf_mac_addr, 6); + } + + return 0; +} + +static int rnpm_vf_reset_msg(struct rnpm_adapter *adapter, u32 vf) +{ + struct rnpm_hw *hw = &adapter->hw; + unsigned char *vf_mac = adapter->vfinfo[vf].vf_mac_addresses; + u32 msgbuf[5]; + // u32 reg_offset, vf_shift; + u8 *addr = (u8 *)(&msgbuf[1]); + + e_info(probe, "VF Reset msg received from vf %d. cmd:0x%x\n", vf, + msgbuf[0]); + + /* reset the filters for the device */ + rnpm_vf_reset_event(adapter, vf); + + /* set vf mac address */ + if (!is_zero_ether_addr(vf_mac)) + rnpm_set_vf_mac(adapter, vf, vf_mac); + + /* enable VF mailbox for further messages */ + adapter->vfinfo[vf].clear_to_send = true; + + /* Enable counting of spoofed packets in the SSVPC register */ + + /* reply to reset with ack and vf mac address */ + msgbuf[0] = RNPM_VF_RESET; + if (!is_zero_ether_addr(vf_mac)) { + msgbuf[0] |= RNPM_VT_MSGTYPE_ACK; + memcpy(addr, vf_mac, ETH_ALEN); + } else { + msgbuf[0] |= RNPM_VT_MSGTYPE_NACK; + dev_warn( + &adapter->pdev->dev, + "VF %d has no MAC address assigned, you may have to assign one manually\n", + vf); + } + + /* Piggyback the multicast filter type so VF can compute the + * correct vectors + */ + msgbuf[RNPM_VF_MC_TYPE_WORD] = 0; + /* setup link status , pause mode, ft padding mode */ + + /* link status */ + // to-do + /* pause mode */ + msgbuf[RNPM_VF_MC_TYPE_WORD] |= (0xff & hw->fc.current_mode) << 16; + if (adapter->priv_flags & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) + msgbuf[RNPM_VF_MC_TYPE_WORD] |= (0x01 << 8); + else + msgbuf[RNPM_VF_MC_TYPE_WORD] |= (0x00 << 8); + /* mc_type */ + msgbuf[RNPM_VF_MC_TYPE_WORD] |= rd32(hw, RNPM_ETH_DMAC_MCSTCTRL) & 0x3; + + msgbuf[RNPM_VF_DMA_VERSION_WORD] = rd32(hw, RNPM_DMA_VERSION); + ; + /* now vf maybe has no irq handler if it is the first reset*/ + rnpm_write_mbx(hw, msgbuf, RNPM_VF_PERMADDR_MSG_LEN, vf); + + return 0; +} + +static int rnpm_set_vf_mac_addr(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + u8 *new_mac = ((u8 *)(&msgbuf[1])); + + if (!is_valid_ether_addr(new_mac)) { + e_warn(drv, "VF %d attempted to set invalid mac\n", vf); + return -1; + } + + if (adapter->vfinfo[vf].pf_set_mac && + memcmp(adapter->vfinfo[vf].vf_mac_addresses, new_mac, ETH_ALEN)) { + e_warn(drv, + "VF %d attempted to override administratively set MAC address\n" + "Reload the VF driver to resume operations\n", + vf); + return -1; + } + + return rnpm_set_vf_mac(adapter, vf, new_mac) < 0; +} + +static int rnpm_set_vf_vlan_msg(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + // struct rnpm_hw *hw = &adapter->hw; + int add = ((msgbuf[0] & RNPM_VT_MSGINFO_MASK) >> RNPM_VT_MSGINFO_SHIFT); + int vid = (msgbuf[1] & RNPM_VLVF_VLANID_MASK); + int err; + //u8 tcs = netdev_get_num_tc(adapter->netdev); + + if (adapter->vfinfo[vf].pf_vlan) { + e_warn(drv, + "VF %d attempted to override administratively set VLAN configuration\n" + "Reload the VF driver to resume operations\n", + vf); + return -1; + } + + if (add) + adapter->vfinfo[vf].vlan_count++; + else if (adapter->vfinfo[vf].vlan_count) + adapter->vfinfo[vf].vlan_count--; + + err = rnpm_set_vf_vlan(adapter, add, vid, vf); + + return err; +} + +static int rnpm_set_vf_vlan_strip_msg(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + struct rnpm_hw *hw = &adapter->hw; + int vlan_strip_on = !!(msgbuf[1] >> 31); + int queue_cnt = msgbuf[1] & 0xffff; + int err = 0, i; + + vf_dbg("strip_on:%d queeu_cnt:%d, %d %d\n", vlan_strip_on, queue_cnt, + msgbuf[2], msgbuf[3]); + + for (i = 0; i < queue_cnt; i++) { + if (vlan_strip_on) + hw_queue_strip_rx_vlan(hw, msgbuf[2 + i], true); + else + hw_queue_strip_rx_vlan(hw, msgbuf[2 + i], false); + } + + return err; +} + +static int rnpm_set_vf_macvlan_msg(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + u8 *new_mac = ((u8 *)(&msgbuf[1])); + int index = (msgbuf[0] & RNPM_VT_MSGINFO_MASK) >> RNPM_VT_MSGINFO_SHIFT; + int err; + + if (adapter->vfinfo[vf].pf_set_mac && index > 0) { + e_warn(drv, + "VF %d requested MACVLAN filter but is administratively denied\n", + vf); + return -1; + } + + /* An non-zero index indicates the VF is setting a filter */ + if (index) { + if (!is_valid_ether_addr(new_mac)) { + e_warn(drv, "VF %d attempted to set invalid mac\n", vf); + return -1; + } + } + + err = rnpm_set_vf_macvlan(adapter, vf, index, new_mac); + if (err == -ENOSPC) + e_warn(drv, + "VF %d has requested a MACVLAN filter but there is no space for it\n", + vf); + + return err < 0; + + return 0; +} + +static int rnpm_negotiate_vf_api(struct rnpm_adapter *adapter, u32 *msgbuf, + u32 vf) +{ + adapter->vfinfo[vf].vf_api = 0; + + return 0; +} + +static int rnpm_get_vf_reg(struct rnpm_adapter *adapter, u32 *msgbuf, u32 vf) +{ + // struct net_device *dev = adapter->netdev; + u32 reg = msgbuf[1]; + + if (reg == 0) { //FIXME check regs + return -1; + } + + msgbuf[1] = rd32(&adapter->hw, reg); + + return 0; +} + +static int rnpm_get_vf_queues(struct rnpm_adapter *adapter, u32 *msgbuf, u32 vf) +{ + struct net_device *dev = adapter->netdev; + // struct rnpm_ring_feature *vmdq = &adapter->ring_feature[RING_F_VMDQ]; + unsigned int default_tc = 0; + u8 num_tcs = netdev_get_num_tc(dev); + + /* verify the PF is supporting the correct APIs */ + + /* only allow 1 Tx queue for bandwidth limiting */ + msgbuf[RNPM_VF_TX_QUEUES] = 1; + msgbuf[RNPM_VF_RX_QUEUES] = 1; + + /* if TCs > 1 determine which TC belongs to default user priority */ + if (num_tcs > 1) + default_tc = netdev_get_prio_tc_map(dev, adapter->default_up); + + /* notify VF of need for VLAN tag stripping, and correct queue */ + if (num_tcs) + msgbuf[RNPM_VF_TRANS_VLAN] = num_tcs; + else if (adapter->vfinfo[vf].pf_vlan || adapter->vfinfo[vf].pf_qos) + msgbuf[RNPM_VF_TRANS_VLAN] = 1; + else + msgbuf[RNPM_VF_TRANS_VLAN] = 0; + + /* notify VF of default queue */ + msgbuf[RNPM_VF_DEF_QUEUE] = default_tc; + + return 0; +} + +static int __maybe_unused rnpm_rcv_msg_from_vf(struct rnpm_adapter *adapter, + u32 vf) +{ + u32 mbx_size = RNPM_VFMAILBOX_SIZE; + u32 msgbuf[RNPM_VFMAILBOX_SIZE]; + struct rnpm_hw *hw = &adapter->hw; + s32 retval; + + retval = rnpm_read_mbx(hw, msgbuf, mbx_size, vf); + if (retval) { + pr_err("Error receiving message from VF\n"); + return retval; + } + vf_dbg("msg[0]=0x%08x\n", msgbuf[0]); + + /* this is a message we already processed, do nothing */ + if (msgbuf[0] & (RNPM_VT_MSGTYPE_ACK | RNPM_VT_MSGTYPE_NACK)) + return retval; + + /* this is a vf reset irq */ + if (msgbuf[0] == RNPM_VF_RESET) + return rnpm_vf_reset_msg(adapter, vf); + + /* until the vf completes a virtual function reset it should not be + * allowed to start any configuration. + */ + if (!adapter->vfinfo[vf].clear_to_send) { + msgbuf[0] |= RNPM_VT_MSGTYPE_NACK; + rnpm_write_mbx(hw, msgbuf, 1, vf); + return retval; + } + + switch ((msgbuf[0] & 0xFFFF)) { + case RNPM_VF_SET_MAC_ADDR: + retval = rnpm_set_vf_mac_addr(adapter, msgbuf, vf); + break; + case RNPM_VF_SET_MULTICAST: + retval = rnpm_set_vf_multicasts(adapter, msgbuf, vf); + break; + case RNPM_VF_SET_VLAN: + retval = rnpm_set_vf_vlan_msg(adapter, msgbuf, vf); + break; + case RNPM_VF_SET_VLAN_STRIP: + retval = rnpm_set_vf_vlan_strip_msg(adapter, msgbuf, vf); + break; + case RNPM_VF_SET_LPE: + retval = rnpm_set_vf_lpe(adapter, msgbuf, vf); + break; + case RNPM_VF_SET_MACVLAN: + retval = rnpm_set_vf_macvlan_msg(adapter, msgbuf, vf); + break; + case RNPM_VF_API_NEGOTIATE: + retval = rnpm_negotiate_vf_api(adapter, msgbuf, vf); + break; + case RNPM_VF_GET_QUEUES: + retval = rnpm_get_vf_queues(adapter, msgbuf, vf); + break; + case RNPM_VF_REG_RD: + retval = rnpm_get_vf_reg(adapter, msgbuf, vf); + break; + case RNPM_PF_REMOVE: + //dbg("vf %d down\n", vf); + adapter->vfinfo[vf].clear_to_send = false; + retval = 1; + break; + default: + e_err(drv, "Unhandled Msg %8.8x\n", msgbuf[0]); + retval = RNPM_ERR_MBX; + break; + } + + /* notify the VF of the results of what it sent us */ + if (retval) + msgbuf[0] |= RNPM_VT_MSGTYPE_NACK; + else + msgbuf[0] |= RNPM_VT_MSGTYPE_ACK; + + msgbuf[0] |= RNPM_VT_MSGTYPE_CTS; + + rnpm_write_mbx(hw, msgbuf, mbx_size, vf); + + return retval; +} + +static void __maybe_unused rnpm_rcv_ack_from_vf(struct rnpm_adapter *adapter, + u32 vf) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 msg = RNPM_VT_MSGTYPE_NACK; + + /* if device isn't clear to send it shouldn't be reading either */ + if (!adapter->vfinfo[vf].clear_to_send) + rnpm_write_mbx(hw, &msg, 1, vf); +} + +void rnpm_msg_task(struct rnpm_pf_adapter *pf_adapter) +{ + rnpm_fw_msg_handler(pf_adapter); +} + +/* try to send mailbox to all active vf */ +void rnpm_msg_post_status(struct rnpm_adapter *adapter, enum PF_STATUS status) +{ + u32 msgbuf[RNPM_VFMAILBOX_SIZE]; + struct rnpm_hw *hw = &adapter->hw; + struct rnpm_mbx_info *mbx = &hw->mbx; + u32 vf; + + for (vf = 0; vf < adapter->num_vfs; vf++) { + if (adapter->vfinfo[vf].clear_to_send) { + dbg("now send msg to vf %d\n", vf); + switch (status) { + case PF_FCS_STATUS: + msgbuf[0] = RNPM_PF_SET_FCS; + if (adapter->netdev->features & NETIF_F_RXFCS) + msgbuf[1] = 1; + else + msgbuf[1] = 0; + break; + case PF_PAUSE_STATUS: + msgbuf[0] = RNPM_PF_SET_PAUSE; + msgbuf[1] = hw->fc.requested_mode; + break; + case PF_FT_PADDING_STATUS: + msgbuf[0] = RNPM_PF_SET_FT_PADDING; + if (adapter->priv_flags & + RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) { + msgbuf[1] = 1; + } else { + msgbuf[1] = 0; + } + + break; + default: + break; + } + //dbg("msg 0 is %x\n", msgbuf[0]); + //dbg("msg 1 is %x\n", msgbuf[1]); + // + mbx->ops.write(hw, msgbuf, 2, vf); + } + } +} + +void rnpm_disable_tx_rx(struct rnpm_adapter *adapter) +{ + // struct rnpm_hw *hw = &adapter->hw; + + /* disable transmit and receive for all vfs */ +} + +void rnpm_ping_all_vfs(struct rnpm_adapter *adapter) +{ + struct rnpm_hw *hw = &adapter->hw; + u32 ping; + int i; + + for (i = 0; i < adapter->num_vfs; i++) { + ping = RNPM_PF_CONTROL_PRING_MSG; + /* only send to active vf */ + //if (adapter->vfinfo[i].clear_to_send) { + ping |= RNPM_VT_MSGTYPE_CTS; + rnpm_write_mbx(hw, &ping, 1, i); + // } + } +} + +int rnpm_get_vf_ringnum(int vf, int num) +{ + //fix me if ring alloc reset + + return (vf * 2 + num); +} + +int rnpm_setup_ring_maxrate(struct rnpm_adapter *adapter, int ring, + u64 max_rate) +{ + u64 x, y, result; +#define RNPM_SAMPING_1SEC_INTERNAL (180000000) + /* set hardware samping internal 1S */ + rnpm_wr_reg(adapter->hw.hw_addr + RNPM_DMA_REG_TX_FLOW_CTRL_TM(ring), + RNPM_SAMPING_1SEC_INTERNAL / 10); + + x = max_rate; + y = do_div(x, 10); + result = x; + result = x * 3; + rnpm_wr_reg(adapter->hw.hw_addr + RNPM_DMA_REG_TX_FLOW_CTRL_TH(ring), + result); + + return 0; +} + +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE +int rnpm_ndo_set_vf_bw(struct net_device *netdev, int vf, + int __always_unused min_tx_rate, int max_tx_rate) +#else +int rnpm_ndo_set_vf_bw(struct net_device *netdev, int vf, int max_tx_rate) +#endif /* HAVE_NDO_SET_VF_MIN_MAX_TX_RATE */ +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + /* limit vf ring rate */ + int ring_max_rate; + int vf_ring; + int link_speed; + + if (vf >= RNPM_MAX_VF_CNT - 1) + return -EINVAL; + + // todo + link_speed = 10000; + //link_speed = rnpm_link_mbps(adapter); + /* rate limit cannot be less than 10Mbs or greater than link speed */ + if (max_tx_rate && ((max_tx_rate <= 10) || (max_tx_rate > link_speed))) + return -EINVAL; + + ring_max_rate = max_tx_rate / PF_RING_CNT_WHEN_IOV_ENABLED; + + vf_ring = rnpm_get_vf_ringnum(vf, 0); + rnpm_setup_ring_maxrate(adapter, vf_ring, ring_max_rate); + vf_ring = rnpm_get_vf_ringnum(vf, 1); + rnpm_setup_ring_maxrate(adapter, vf_ring, ring_max_rate); + return 0; +} + +int rnpm_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + if (!is_valid_ether_addr(mac) || (vf >= adapter->num_vfs)) + return -EINVAL; + adapter->vfinfo[vf].pf_set_mac = true; + dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", mac, vf); + if (test_bit(__RNPM_DOWN, &adapter->state)) { + dev_warn( + &adapter->pdev->dev, + "The VF MAC address has been set but the PFis not up.\n"); + } + return rnpm_set_vf_mac(adapter, vf, mac); +} + +static int __maybe_unused rnpm_link_mbps(struct rnpm_adapter *adapter) +{ + switch (adapter->link_speed) { + case RNPM_LINK_SPEED_100_FULL: + return 100; + case RNPM_LINK_SPEED_1GB_FULL: + return 1000; + case RNPM_LINK_SPEED_10GB_FULL: + return 10000; + default: + return 0; + } +} + +int rnpm_ndo_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi) +{ + struct rnpm_adapter *adapter = netdev_priv(netdev); + + if (vf >= adapter->num_vfs) + return -EINVAL; + ivi->vf = vf; + memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN); + //ivi->tx_rate = adapter->vfinfo[vf].tx_rate; + ivi->vlan = adapter->vfinfo[vf].pf_vlan; + ivi->qos = adapter->vfinfo[vf].pf_qos; +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE + ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled; +#endif + return 0; +} +int rnpm_pci_sriov_configure(struct pci_dev *dev, int num_vfs) +{ + vf_dbg("\n\n !!!! %s:%d num_vfs:%d\n", __func__, __LINE__, num_vfs); + if (num_vfs == 0) + return rnpm_pci_sriov_disable(dev); + else + return rnpm_pci_sriov_enable(dev, num_vfs); +} diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.h b/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.h new file mode 100644 index 000000000000..81a0da152349 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_sriov.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#ifndef _RNPM_SRIOV_H_ +#define _RNPM_SRIOV_H_ + +void rnpm_restore_vf_multicasts(struct rnpm_adapter *adapter); +void rnpm_msg_task(struct rnpm_pf_adapter *adapter); +int rnpm_vf_configuration(struct pci_dev *pdev, unsigned int event_mask); +void rnpm_disable_tx_rx(struct rnpm_adapter *adapter); +void rnpm_ping_all_vfs(struct rnpm_adapter *adapter); +int rnpm_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac); +void rnpm_msg_post_status(struct rnpm_adapter *adapter, enum PF_STATUS status); +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE +int rnpm_ndo_set_vf_bw(struct net_device *netdev, int vf, + int __always_unused min_tx_rate, int max_tx_rate); +#else +int rnpm_ndo_set_vf_bw(struct net_device *netdev, int vf, int max_tx_rate); +#endif /* HAVE_NDO_SET_VF_MIN_MAX_TX_RATE */ + +int rnpm_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); +int rnpm_ndo_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi); +void rnpm_check_vf_rate_limit(struct rnpm_adapter *adapter); +int rnpm_disable_sriov(struct rnpm_adapter *adapter); +#ifdef CONFIG_PCI_IOV +void rnpm_enable_sriov(struct rnpm_adapter *adapter); +#endif +int rnpm_pci_sriov_configure(struct pci_dev *dev, int num_vfs); + +static inline void rnpm_set_vmvir(struct rnpm_adapter *adapter, u16 vid, + u16 qos, u32 vf) +{ + // struct rnpm_hw *hw = &adapter->hw; +} + +#endif /* _RNPM_SRIOV_H_ */ diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_sysfs.c b/drivers/net/ethernet/mucse/rnpm/rnpm_sysfs.c new file mode 100644 index 000000000000..f3438ed18ba9 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpm/rnpm_sysfs.c @@ -0,0 +1,1842 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2022 - 2024 Mucse Corporation. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rnpm.h" +#include "rnpm_common.h" +#include "rnpm_type.h" + +#include "rnpm_mbx.h" +#include "rnpm_mbx_fw.h" + +struct maintain_req { + int magic; +#define MAINTAIN_MAGIC 0xa6a7a8a9 + + int cmd; + int arg0; + int req_data_bytes; + int reply_bytes; +} __packed; + +struct ucfg_mac_sn { + unsigned char macaddr[64]; + unsigned char sn[32]; + int magic; +#define MAC_SN_MAGIC 0x87654321 + char rev[52]; + unsigned char pn[32]; +} __packed __aligned(4); + +u32 bar4_reg_val; +u32 bar4_reg_addr; +u32 pcs_phy_num; +int pcs_cnt; + +#define to_net_device(n) container_of(n, struct net_device, dev) + +#define PHY_EXT_REG_FLAG 0x80000000 +static u32 is_phy_ext_reg; +static u32 phy_reg; +#ifndef NO_BIT_ATTRS +static ssize_t maintain_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + // struct rnpm_hw *hw = &adapter->hw; + int rbytes = count; + + if (adapter->maintain_buf == NULL) + return 0; + + if (off + count > adapter->maintain_buf_len) + rbytes = adapter->maintain_buf_len - off; + memcpy(buf, adapter->maintain_buf + off, rbytes); + + // end-of-buf + if ((off + rbytes) >= adapter->maintain_buf_len) { + kfree(adapter->maintain_buf); + adapter->maintain_buf = NULL; + adapter->maintain_buf_len = 0; + } + + return rbytes; +} + +static ssize_t maintain_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + // int ret; + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + struct maintain_req *req; + void *dma_buf = NULL; + dma_addr_t dma_phy; + int bytes; + + if (off == 0) { + if (count < sizeof(*req)) + return -EINVAL; + req = (struct maintain_req *)buf; + if (req->magic != MAINTAIN_MAGIC) + return -EINVAL; + bytes = max_t(int, req->req_data_bytes, req->reply_bytes); + bytes += sizeof(*req); + + // free no readed buf + kfree(adapter->maintain_buf); + adapter->maintain_buf = NULL; + adapter->maintain_buf_len = 0; + + dma_buf = dma_alloc_coherent(&hw->pdev->dev, bytes, &dma_phy, + GFP_ATOMIC); + if (!dma_buf) + return -ENOMEM; + + adapter->maintain_dma_buf = dma_buf; + adapter->maintain_dma_phy = dma_phy; + adapter->maintain_dma_size = bytes; + adapter->maintain_in_bytes = req->req_data_bytes + sizeof(*req); + + memcpy(dma_buf + off, buf, count); + + if (count < adapter->maintain_in_bytes) + return count; + } + + dma_buf = adapter->maintain_dma_buf; + dma_phy = adapter->maintain_dma_phy; + req = (struct maintain_req *)dma_buf; + + memcpy(dma_buf + off, buf, count); + + // all data got, send req + if ((off + count) >= adapter->maintain_in_bytes) { + int reply_bytes = req->reply_bytes; + // send req + err = rnpm_maintain_req(hw, req->cmd, req->arg0, + req->req_data_bytes, req->reply_bytes, + dma_phy); + if (err != 0) + goto err_quit; + // req can't be acces, a + // copy data for read + if (reply_bytes > 0) { + adapter->maintain_buf_len = reply_bytes; + adapter->maintain_buf = + kmalloc(adapter->maintain_buf_len, GFP_KERNEL); + if (!adapter->maintain_buf) { + netdev_err(netdev, + "No Memory for maintain buf:%d\n", + adapter->maintain_buf_len); + err = -ENOMEM; + + goto err_quit; + } + memcpy(adapter->maintain_buf, dma_buf, reply_bytes); + // buf_dump("rx", adapter->maintain_buf, reply_bytes); + } + + if (dma_buf) { + //pci_free_consistent( + dma_free_coherent(&hw->pdev->dev, + adapter->maintain_dma_size, dma_buf, + dma_phy); + } + adapter->maintain_dma_buf = NULL; + } + + return count; +err_quit: + if (dma_buf) { + //pci_free_consistent( + dma_free_coherent(&hw->pdev->dev, adapter->maintain_dma_size, + dma_buf, dma_phy); + adapter->maintain_dma_buf = NULL; + } + return err; +} + +/* some centos kernel maybe error use BIN_ATTR_RW */ +//static BIN_ATTR_RW(maintain, 1 * 1024 * 1024); +static BIN_ATTR(maintain, 0644, maintain_read, maintain_write, 1 * 1024 * 1024); +#endif + +static ssize_t active_vid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u16 current_vid = 0; + u16 vid = 0; + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + u8 vfnum = RNPM_MAX_VF_CNT - 1; //use last-vf's table entry. the las + + if ((adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) { + current_vid = rd32(hw, RNPM_DMA_PORT_VEB_VID_TBL(adapter->port, + vfnum)); + } + for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID) { + ret += sprintf(buf + ret, "%u%s ", vid, + (current_vid == vid ? "*" : "")); + } + ret += sprintf(buf + ret, "\n"); + return ret; +} + +static ssize_t active_vid_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u16 vid; + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw __maybe_unused *hw = &adapter->hw; + u8 __maybe_unused vfnum = + RNPM_MAX_VF_CNT - 1; // use last-vf's table entry. the las + int __maybe_unused port = 0; + + if (!(adapter->flags & RNPM_FLAG_SRIOV_ENABLED)) + return -EIO; + + if (kstrtou16(buf, 0, &vid) != 0) + return -EINVAL; + if ((vid < 4096) && test_bit(vid, adapter->active_vlans)) { + if (rd32(hw, RNPM_DMA_VERSION) >= 0x20201231) { + for (port = 0; port < 4; port++) + wr32(hw, RNPM_DMA_PORT_VEB_VID_TBL(port, vfnum), + vid); + } else { + wr32(hw, + RNPM_DMA_PORT_VEB_VID_TBL(adapter->port, vfnum), + vid); + } + err = 0; + } + return err ? err : count; +} +static DEVICE_ATTR_RW(active_vid); + +static ssize_t pf_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; + + set_bit(RNPM_PF_RESET, &pf_adapter->flags); + + return count; +} + +static DEVICE_ATTR_WO(pf_reset); + +static inline int pn_sn_dlen(char *v, int v_len) +{ + int i, len = 0; + + for (i = 0; i < v_len; i++) { + if (isascii(v[i])) + len++; + break; + } + return len; +} + +int rnpm_mbx_get_pn_sn(struct rnpm_hw *hw, char pn[33], char sn[33]) +{ + struct maintain_req *req; + void *dma_buf = NULL; + dma_addr_t dma_phy; + struct ucfg_mac_sn *cfg; + + int err = 0, bytes = sizeof(*req) + sizeof(struct ucfg_mac_sn); + + memset(pn, 0, 33); + memset(sn, 0, 33); + + dma_buf = + dma_alloc_coherent(&hw->pdev->dev, bytes, &dma_phy, GFP_KERNEL); + if (!dma_buf) + return -ENOMEM; + + req = (struct maintain_req *)dma_buf; + memset(dma_buf, 0, bytes); + cfg = (struct ucfg_mac_sn *)(req + 1); + req->magic = MAINTAIN_MAGIC; + req->cmd = 0; // READ + req->arg0 = 3; // PARTITION 3 + req->req_data_bytes = 0; + req->reply_bytes = bytes - sizeof(*req); + + err = rnpm_maintain_req(hw, req->cmd, req->arg0, req->req_data_bytes, + req->reply_bytes, dma_phy); + if (err != 0) + goto err_quit; + if (cfg->magic == MAC_SN_MAGIC) { + int sz = pn_sn_dlen(cfg->pn, 32); + + if (sz) { + memcpy(pn, cfg->pn, sz); + pn[sz] = 0; + } + sz = pn_sn_dlen(cfg->sn, 32); + if (sz) { + memcpy(sn, cfg->sn, sz); + sn[sz] = 0; + } + } + +err_quit: + if (dma_buf) { + // pci_free_consistent( + dma_free_coherent(&hw->pdev->dev, bytes, dma_buf, dma_phy); + } + + return 0; +} + +static ssize_t own_vpd_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + char pn[33] = { 0 }, sn[33] = { 0 }; + char *prod_name; + + rnpm_mbx_get_pn_sn(hw, pn, sn); + + if ((adapter->pdev->device & 0xff) == 0x20) { + prod_name = + "Ethernet Controller N10 Series for 10GbE (Quad-port)"; + } else if ((adapter->pdev->device & 0xff) == 0x21) { + prod_name = + "Ethernet Controller N400 Series for 1GbE (Quad-port)"; + } else if ((adapter->pdev->device & 0xff) == 0x60) { + prod_name = + "Ethernet Controller N10 Series for 10GbE (Oct-port)"; + } else { + prod_name = "Ethernet Controller N10 Series for 10GbE"; + } + + ret += sprintf(buf + ret, "Product Name: %s\n", prod_name); + + ret += sprintf(buf + ret, "[PN] Part number: %s\n", pn); + ret += sprintf(buf + ret, "[SN] Serial number: %s\n", sn); + + return ret; +} +static DEVICE_ATTR_RO(own_vpd); + +//========== + +/* show logical and physical ring num correspondence */ +static ssize_t ring_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_ring *ring; + int ret = 0, i; + + ret += sprintf( + buf + ret, + "show %s logical <-> physical ring num (Decimal) correspondence:\n", + netdev->name); + for (i = 0; i < netdev->real_num_tx_queues; i++) { + ring = adapter->tx_ring[i]; + ret += sprintf(buf + ret, "%03d %03d\n", i, + ring->rnpm_queue_idx); + } + return ret; +} +static DEVICE_ATTR_RO(ring_num); + +static ssize_t port_idx_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + // struct rnpm_hw *hw = &adapter->hw; + + ret += sprintf(buf, "%d\n", adapter->portid_of_card); + return ret; +} +static DEVICE_ATTR_RO(port_idx); + +static ssize_t nr_lane_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + // struct rnpm_hw *hw = &adapter->hw; + + ret += sprintf(buf, "%d\n", adapter->lane); + return ret; +} +static DEVICE_ATTR_RO(nr_lane); + +static ssize_t debug_linkstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + ret += sprintf(buf, "%d %d dumy:0x%x up-flag:%d carry:%d\n", + adapter->link_up, adapter->hw.link, rd32(hw, 0xc), + adapter->flags & RNPM_FLAG_NEED_LINK_UPDATE, + netif_carrier_ok(netdev)); + return ret; +} +static DEVICE_ATTR_RO(debug_linkstat); + +static ssize_t debug_aptstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + ret += sprintf( + buf, + "stat:%d %d dumy:0x%x up-flag:%d carry:%d\n" + "apt:flags:0x%x flags2:0x%x state:0x%lx timercnt:%u service cnt:%u\n" + "pf_apt:flags:0x%lx state:0x%lx timercnt:%u\n", + adapter->link_up, adapter->hw.link, rd32(hw, 0xc), + adapter->flags & RNPM_FLAG_NEED_LINK_UPDATE, + netif_carrier_ok(netdev), adapter->flags, adapter->flags2, + adapter->state, adapter->timer_count, adapter->service_count, + adapter->pf_adapter->flags, adapter->pf_adapter->state, + adapter->pf_adapter->timer_count); + return ret; +} +static DEVICE_ATTR_RO(debug_aptstat); + +static ssize_t sfp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) { + ret += sprintf(buf, " IO Error\n"); + } else { + ret += sprintf( + buf, "mod-abs:%d\ntx-fault:%d\ntx-dis:%d\nrx-los:%d\n", + adapter->sfp.mod_abs, adapter->sfp.fault, + adapter->sfp.tx_dis, adapter->sfp.los); + } + + return ret; +} +static DEVICE_ATTR_RO(sfp); + +static ssize_t pci_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + int gen = 3, lanes = 8; + + if (count > 30) + return -EINVAL; + + if (sscanf(buf, "gen%dx%d", &gen, &lanes) != 2) { + e_dev_info("Error: invalid input. example: gen3x8\n"); + return -EINVAL; + } + if (gen > 3 || lanes > 8) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_PCI_LANE, gen, lanes, 0, 0); + + return err ? err : count; +} + +static ssize_t pci_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) + ret += sprintf(buf, " IO Error\n"); + else + ret += sprintf(buf, "gen%dx%d\n", hw->pci_gen, hw->pci_lanes); + + return ret; +} +static DEVICE_ATTR_RW(pci); + +static ssize_t prbs_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + long prbs = 0; + + if (kstrtol(buf, 10, &prbs)) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_PRBS, prbs, 0, 0, 0); + + return err ? err : count; +} +static DEVICE_ATTR_WO(prbs); + +static ssize_t sfp_tx_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + long enable = 0; + + if (kstrtol(buf, 10, &enable)) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_SFP_TX_DISABLE, !!enable, 0, 0, 0); + + return err ? err : count; +} + +static ssize_t sfp_tx_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) + ret += sprintf(buf, " IO Error\n"); + else + ret += sprintf(buf, "%d\n", adapter->sfp.tx_dis); + + return ret; +} +static DEVICE_ATTR_RW(sfp_tx_disable); + +static ssize_t link_traing_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + long enable = 0; + + if (kstrtol(buf, 10, &enable)) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_LINK_TRAING, !!enable, 0, 0, 0); + + return err ? err : count; +} + +static ssize_t link_traing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) + ret += sprintf(buf, " IO Error\n"); + else + ret += sprintf(buf, "%d\n", adapter->link_traing); + + return ret; +} +static DEVICE_ATTR_RW(link_traing); + +static ssize_t fec_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + long enable = 0; + + if (kstrtol(buf, 10, &enable)) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_FEC, !!enable, 0, 0, 0); + + return err ? err : count; +} + +static ssize_t fec_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) + ret += sprintf(buf, " IO Error\n"); + else + ret += sprintf(buf, "%d\n", adapter->fec); + + return ret; +} +static DEVICE_ATTR_RW(fec); + +static ssize_t autoneg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + long enable = 0; + + if (kstrtol(buf, 10, &enable)) + return -EINVAL; + + err = rnpm_set_lane_fun(hw, LANE_FUN_AN, !!enable, 0, 0, 0); + + return err ? err : count; +} + +static ssize_t autoneg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + + if (rnpm_mbx_get_lane_stat(hw) != 0) + ret += sprintf(buf, " IO Error\n"); + else + ret += sprintf(buf, "%d\n", adapter->an); + + return ret; +} +static DEVICE_ATTR_RW(autoneg); + +static ssize_t si_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = -EINVAL; + struct net_device *netdev = to_net_device(dev); + struct rnpm_adapter *adapter = netdev_priv(netdev); + struct rnpm_hw *hw = &adapter->hw; + int si_main = -1, si_pre = -1, si_post = -1, si_txboost = -1; + int cnt; + + if (count > 100) { + e_dev_info("Error: Input size >100: too large\n"); + return -EINVAL; + } + cnt = sscanf(buf, "%u %u %u %u", &si_main, &si_pre, &si_post, + &si_txboost); + if (cnt != 4) { + e_dev_info( + "Error: Invalid Input:
  \n");
+		return -EINVAL;
+	}
+	if (si_main > 63 || si_pre > 63 || si_post > 63) {
+		e_dev_info("Error: Invalid value. should in 0~63\n");
+		return -EINVAL;
+	}
+	if (si_txboost > 16) {
+		e_dev_info("Error: Invalid txboost. should in 0~15\n");
+		return -EINVAL;
+	}
+	err = rnpm_set_lane_fun(hw, LANE_FUN_SI, si_main, si_pre, si_post,
+				si_txboost);
+
+	return err ? err : count;
+}
+
+static ssize_t si_show(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	int ret = 0;
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+
+	if (rnpm_mbx_get_lane_stat(hw) != 0)
+		ret += sprintf(buf, " IO Error\n");
+	else
+		ret += sprintf(buf, "main:%u pre:%u post:%u tx_boost:%u\n",
+			       adapter->si.main, adapter->si.pre,
+			       adapter->si.post, adapter->si.tx_boost);
+
+	return ret;
+}
+static DEVICE_ATTR_RW(si);
+
+static ssize_t temperature_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	int ret = 0, temp = 0, voltage = 0;
+
+	temp = rnpm_mbx_get_temp(hw, &voltage);
+
+	ret += sprintf(buf, "temp:%d oC  volatage:%d mV\n", temp, voltage);
+	return ret;
+}
+static DEVICE_ATTR_RO(temperature);
+
+static ssize_t tx_counter_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u32 val = 0;
+	int ret = 0;
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+
+	ret += sprintf(buf + ret, "tx counters\n");
+	ret += sprintf(buf + ret, "ring0-tx:\n");
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_LEN(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "len:", RNPM_DMA_REG_TX_DESC_BUF_LEN(0), val);
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "head:", RNPM_DMA_REG_TX_DESC_BUF_HEAD(0), val);
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "tail:", RNPM_DMA_REG_TX_DESC_BUF_TAIL(0), val);
+
+	ret += sprintf(buf + ret, "ring1-tx:\n");
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_LEN(1));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "len:", RNPM_DMA_REG_TX_DESC_BUF_LEN(1), val);
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD(1));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "head:", RNPM_DMA_REG_TX_DESC_BUF_HEAD(1), val);
+
+	val = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL(1));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "tail:", RNPM_DMA_REG_TX_DESC_BUF_TAIL(1), val);
+
+	ret += sprintf(buf + ret, "to_1to4_p1:\n");
+
+	val = rd32(hw, RNPM_ETH_1TO4_INST0_IN_PKTS);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "emac_in:", RNPM_ETH_1TO4_INST0_IN_PKTS, val);
+
+	val = rd32(hw, RNPM_ETH_IN_0_TX_PKT_NUM(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "emac_send:", RNPM_ETH_IN_0_TX_PKT_NUM(0), val);
+
+	ret += sprintf(buf + ret, "to_1to4_p2:\n");
+
+	val = rd32(hw, RNPM_ETH_IN_1_TX_PKT_NUM(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "sop_pkt:", RNPM_ETH_IN_1_TX_PKT_NUM(0), val);
+
+	val = rd32(hw, RNPM_ETH_IN_2_TX_PKT_NUM(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "eop_pkt:", RNPM_ETH_IN_2_TX_PKT_NUM(0), val);
+
+	val = rd32(hw, RNPM_ETH_IN_3_TX_PKT_NUM(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "send_terr:", RNPM_ETH_IN_3_TX_PKT_NUM(0), val);
+
+	ret += sprintf(buf + ret, "to_tx_trans(phy):\n");
+
+	val = rd32(hw, RNPM_ETH_EMAC_TX_TO_PHY_PKTS(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "in:", RNPM_ETH_EMAC_TX_TO_PHY_PKTS(0), val);
+
+	val = rd32(hw, RNPM_ETH_TXTRANS_PTP_PKT_NUM(0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "out:", RNPM_ETH_TXTRANS_PTP_PKT_NUM(0), val);
+
+	ret += sprintf(buf + ret, "mac:\n");
+
+	val = rd32(hw, 0x1081c);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", "tx:", 0x1081c,
+		       val);
+
+	val = rd32(hw, 0x1087c);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "underflow_err:", 0x1087c, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT0_SOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port0_txtrans_sop:", RNPM_ETH_TX_DEBUG_PORT0_SOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT0_EOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port0_txtrans_eop:", RNPM_ETH_TX_DEBUG_PORT0_EOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT1_SOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port1_txtrans_sop:", RNPM_ETH_TX_DEBUG_PORT1_SOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT1_EOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port1_txtrans_eop:", RNPM_ETH_TX_DEBUG_PORT1_EOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT2_SOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port2_txtrans_sop:", RNPM_ETH_TX_DEBUG_PORT2_SOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT2_EOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port2_txtrans_eop:", RNPM_ETH_TX_DEBUG_PORT2_EOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT3_SOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port3_txtrans_sop:", RNPM_ETH_TX_DEBUG_PORT3_SOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PORT3_EOP);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "port3_txtrans_eop:", RNPM_ETH_TX_DEBUG_PORT3_EOP, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_EMPTY);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "tx_empty:", RNPM_ETH_TX_DEBUG_EMPTY, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_PROG_FULL);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "tx_prog_full:", RNPM_ETH_TX_DEBUG_PROG_FULL, val);
+
+	val = rd32(hw, RNPM_ETH_TX_DEBUG_FULL);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "tx_full:", RNPM_ETH_TX_DEBUG_FULL, val);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RO(tx_counter);
+
+static ssize_t rx_counter_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u32 val = 0, port = 0;
+	int ret = 0;
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+
+	ret += sprintf(buf + ret, "rx counters\n");
+	for (port = 0; port < 4; port++) {
+		ret += sprintf(buf + ret, "emac_rx_trans (port:%d):\n", port);
+
+		val = rd32(hw, RNPM_RXTRANS_RX_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "pkts:", RNPM_RXTRANS_RX_PKTS(port), val);
+
+		val = rd32(hw, RNPM_RXTRANS_DROP_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "drop:", RNPM_RXTRANS_DROP_PKTS(port), val);
+
+		val = rd32(hw, RNPM_RXTRANS_WDT_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "wdt_err:", RNPM_RXTRANS_WDT_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_CODE_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "code_err:", RNPM_RXTRANS_CODE_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_CRC_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "crc_err:", RNPM_RXTRANS_CRC_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_SLEN_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "slen_err:", RNPM_RXTRANS_SLEN_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_GLEN_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "glen_err:", RNPM_RXTRANS_GLEN_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_IPH_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "iph_err:", RNPM_RXTRANS_IPH_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_CSUM_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "csum_err:", RNPM_RXTRANS_CSUM_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_LEN_ERR_PKTS(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "len_err:", RNPM_RXTRANS_LEN_ERR_PKTS(port),
+			       val);
+
+		val = rd32(hw, RNPM_RXTRANS_CUT_ERR_PKTS(port));
+		ret += sprintf(
+			buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			"trans_cut_err:", RNPM_RXTRANS_CUT_ERR_PKTS(port), val);
+
+		val = rd32(hw, RNPM_RXTRANS_EXCEPT_BYTES(port));
+		ret += sprintf(
+			buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			"expt_byte_err:", RNPM_RXTRANS_EXCEPT_BYTES(port), val);
+
+		val = rd32(hw, RNPM_RXTRANS_G1600_BYTES_PKTS(port));
+		ret += sprintf(
+			buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			">1600Byte:", RNPM_RXTRANS_G1600_BYTES_PKTS(port), val);
+	}
+
+	ret += sprintf(buf + ret, "gather:\n");
+	val = rd32(hw, RNPM_ETH_TOTAL_GAT_RX_PKT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "total_in_pkts:", RNPM_ETH_TOTAL_GAT_RX_PKT_NUM, val);
+
+	port = 0;
+	val = rd32(hw, RNPM_ETH_RX_PKT_NUM(port));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "to_nxt_mdodule:", RNPM_ETH_RX_PKT_NUM(port), val);
+
+	for (port = 0; port < 4; port++) {
+		u8 pname[16] = { 0 };
+
+		val = rd32(hw, RNPM_ETH_RX_PKT_NUM(port));
+		sprintf(pname, "p%d-rx:", port);
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", pname,
+			       RNPM_ETH_RX_PKT_NUM(port), val);
+	}
+
+	for (port = 0; port < 4; port++) {
+		u8 pname[16] = { 0 };
+
+		val = rd32(hw, RNPM_ETH_RX_DROP_PKT_NUM(port));
+		sprintf(pname, "p%d-drop:", port);
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", pname,
+			       RNPM_ETH_RX_DROP_PKT_NUM(port), val);
+	}
+
+	ret += sprintf(buf + ret, "ip-parse:\n");
+
+	val = rd32(hw, RNPM_ETH_PKT_EGRESS_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "pkg_egree:", RNPM_ETH_PKT_EGRESS_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_IP_HDR_LEN_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "L3_len_err:", RNPM_ETH_PKT_IP_HDR_LEN_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_IP_PKT_LEN_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "ip_hdr_err:", RNPM_ETH_PKT_IP_PKT_LEN_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_L3_HDR_CHK_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "l3-csum-err:", RNPM_ETH_PKT_L3_HDR_CHK_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_L4_HDR_CHK_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "l4-csum-err:", RNPM_ETH_PKT_L4_HDR_CHK_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_SCTP_CHK_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "sctp-err:", RNPM_ETH_PKT_SCTP_CHK_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_VLAN_ERR_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "vlan-err:", RNPM_ETH_PKT_VLAN_ERR_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_EXCEPT_SHORT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "except_short_num:", RNPM_ETH_PKT_EXCEPT_SHORT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_PKT_PTP_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "ptp:", RNPM_ETH_PKT_PTP_NUM, val);
+
+	ret += sprintf(buf + ret, "to-indecap:\n");
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_IN_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "*in engin*:", RNPM_ETH_DECAP_PKT_IN_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_OUT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "*out engin*:", RNPM_ETH_DECAP_PKT_OUT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_DMAC_OUT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "to-dma/host:", RNPM_ETH_DECAP_DMAC_OUT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_BMC_OUT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "to-bmc:", RNPM_ETH_DECAP_BMC_OUT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_SW_OUT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "to-switch:", RNPM_ETH_DECAP_SW_OUT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_MIRROR_OUT_NUM);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "bmc+host:", RNPM_ETH_DECAP_MIRROR_OUT_NUM, val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(0x0));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "err_drop:", RNPM_ETH_DECAP_PKT_DROP_NUM(0x0), val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(1));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "plicy_drop:", RNPM_ETH_DECAP_PKT_DROP_NUM(1), val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(2));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "dmac_drop:", RNPM_ETH_DECAP_PKT_DROP_NUM(2), val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(3));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "bmc_drop:", RNPM_ETH_DECAP_PKT_DROP_NUM(3), val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(4));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "sw_drop:", RNPM_ETH_DECAP_PKT_DROP_NUM(4), val);
+
+	val = rd32(hw, RNPM_ETH_DECAP_PKT_DROP_NUM(5));
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "rm_vlane_num:", RNPM_ETH_DECAP_PKT_DROP_NUM(5), val);
+
+	ret += sprintf(buf + ret, "dma-2-host:\n");
+
+	val = rd32(hw, 0x264);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", "fifo equ:", 0x264,
+		       val);
+
+	val = rd32(hw, 0x268);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", "fifo deq:", 0x268,
+		       val);
+
+	val = rd32(hw, 0x114);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+		       "unexpt_abtring:", 0x114, val);
+
+	val = rd32(hw, 0x288);
+	ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n", "pci2host:", 0x288,
+		       val);
+
+	for (port = 0; port < 4; port++) {
+		ret += sprintf(buf + ret, "rx-ring%d:\n", port);
+
+		val = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "head:", RNPM_DMA_REG_RX_DESC_BUF_HEAD(port),
+			       val);
+
+		val = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_TAIL(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "tail:", RNPM_DMA_REG_RX_DESC_BUF_TAIL(port),
+			       val);
+
+		val = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_LEN(port));
+		ret += sprintf(buf + ret, "\t %16s 0x%08x: 0x%x\n",
+			       "len:", RNPM_DMA_REG_RX_DESC_BUF_LEN(port), val);
+	}
+
+	return ret;
+}
+
+static DEVICE_ATTR_RO(rx_counter);
+
+static ssize_t bar4_reg_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	int ret = 0;
+
+	ret += sprintf(buf + ret, "addr=0x%x, val=0x%x\n", bar4_reg_addr,
+		       bar4_reg_val);
+	return ret;
+}
+
+static ssize_t bar4_reg_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	int rc;
+
+	rc = kstrtou32(buf, 0, &bar4_reg_addr);
+	if (rc)
+		return -EINVAL;
+	bar4_reg_val = rd32(hw, bar4_reg_addr);
+
+	return count;
+}
+static DEVICE_ATTR_RW(bar4_reg);
+
+static struct pci_dev *pcie_find_root_port_old(struct pci_dev *dev)
+{
+	while (1) {
+		if (!pci_is_pcie(dev))
+			break;
+		if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+			return dev;
+		if (!dev->bus->self)
+			break;
+		dev = dev->bus->self;
+	}
+	return NULL;
+}
+
+static ssize_t root_slot_info_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	int ret = 0;
+	struct pci_dev *root_pdev = pcie_find_root_port_old(adapter->pdev);
+
+	if (root_pdev) {
+		ret += sprintf(buf + ret, "%02x:%02x.%x\n",
+			       root_pdev->bus->number,
+			       PCI_SLOT(root_pdev->devfn),
+			       PCI_FUNC(root_pdev->devfn));
+	}
+	return ret;
+}
+
+static DEVICE_ATTR_RO(root_slot_info);
+
+static ssize_t phy_statistics_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	struct phy_statistics ps;
+
+	memset(&ps, 0, sizeof(ps));
+
+	if (rnpm_mbx_get_phy_statistics(hw, (u8 *)&ps) != 0)
+		return 0;
+
+	ret += sprintf(buf + ret, "RX crc good   (64~1518) : %u\n",
+		       ps.yt.pkg_ib_valid);
+	ret += sprintf(buf + ret, "RX crc good   (>1518)   : %u\n",
+		       ps.yt.pkg_ib_os_good);
+	ret += sprintf(buf + ret, "RX crc good   (<64)     : %u\n",
+		       ps.yt.pkg_ib_us_good);
+	ret += sprintf(buf + ret, "RX crc wrong  (64~1518) : %u\n",
+		       ps.yt.pkg_ib_err);
+	ret += sprintf(buf + ret, "RX crc wrong  (>1518)   : %u\n",
+		       ps.yt.pkg_ib_os_bad);
+	ret += sprintf(buf + ret, "RX crc wrong  (<64)     : %u\n",
+		       ps.yt.pkg_ib_frag);
+	ret += sprintf(buf + ret, "RX SFD missed (nosfd)   : %u\n",
+		       ps.yt.pkg_ib_nosfd);
+	ret += sprintf(buf + ret, "TX crc good   (64~1518) : %u\n",
+		       ps.yt.pkg_ob_valid);
+	ret += sprintf(buf + ret, "TX crc good   (>1518)   : %u\n",
+		       ps.yt.pkg_ob_os_good);
+	ret += sprintf(buf + ret, "TX crc good   (<64)     : %u\n",
+		       ps.yt.pkg_ob_us_good);
+	ret += sprintf(buf + ret, "TX crc wrong  (64~1518) : %u\n",
+		       ps.yt.pkg_ob_err);
+	ret += sprintf(buf + ret, "TX crc wrong  (>1518)   : %u\n",
+		       ps.yt.pkg_ob_os_bad);
+	ret += sprintf(buf + ret, "TX crc wrong  (<64)     : %u\n",
+		       ps.yt.pkg_ob_frag);
+	ret += sprintf(buf + ret, "TX SFD missed (nosfd)   : %u\n",
+		       ps.yt.pkg_ob_nosfd);
+
+	return ret;
+}
+static DEVICE_ATTR_RO(phy_statistics);
+
+static ssize_t pcs_reg_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	// eg: echo "phy reg val" > pcs_reg
+	// sscanf
+	u32 pcs_phy_regs[] = {
+		0x00040000, 0x00041000, 0x00042000, 0x00043000,
+		0x00040000, 0x00041000, 0x00042000, 0x00043000,
+	};
+
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	u32 reg_hi = 0, reg_lo = 0, pcs_base_regs = 0;
+
+	if (count > 64) {
+		e_dev_info("Error: Input size >100: too large\n");
+		return -EINVAL;
+	}
+
+	pcs_cnt = sscanf(buf, "%u %x %x", &pcs_phy_num, &bar4_reg_addr,
+			 &bar4_reg_val);
+
+	if (pcs_cnt != 2 && pcs_cnt != 3) {
+		e_dev_info(
+			"Error: Invalid Input: read phy x reg 0xXXX or write phy x reg ");
+		e_dev_info("0xXXX val 0xXXX\n");
+		return -EINVAL;
+	}
+
+	if (pcs_phy_num > 8) {
+		e_dev_info("Error: Invalid value. should in 0~7\n");
+		return -EINVAL;
+	}
+
+	switch (pcs_cnt) {
+	case 2:
+		reg_hi = bar4_reg_addr >> 8;
+		reg_lo = (bar4_reg_addr & 0xff) << 2;
+		pcs_base_regs = pcs_phy_regs[pcs_phy_num];
+		wr32(hw, pcs_base_regs + (0xff << 2), reg_hi);
+		bar4_reg_val = rd32(hw, pcs_base_regs + reg_lo);
+		break;
+	case 3:
+		reg_hi = bar4_reg_addr >> 8;
+		reg_lo = (bar4_reg_addr & 0xff) << 2;
+		pcs_base_regs = pcs_phy_regs[pcs_phy_num];
+		wr32(hw, pcs_base_regs + (0xff << 2), reg_hi);
+		wr32(hw, pcs_base_regs + reg_lo, bar4_reg_val);
+		break;
+	default:
+		e_dev_info("Error: Invalid value. pcs_cnt=%d\n", pcs_cnt);
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t pcs_reg_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	int ret = 0;
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+
+	switch (pcs_cnt) {
+	case 2:
+		ret += sprintf(buf, "read phy %u reg 0x%x val 0x%x\n",
+			       pcs_phy_num, bar4_reg_addr, bar4_reg_val);
+		break;
+	case 3:
+		ret += sprintf(buf, "write phy %u reg 0x%x val 0x%x\n",
+			       pcs_phy_num, bar4_reg_addr, bar4_reg_val);
+		break;
+	default:
+		e_dev_info("Error: Invalid pcs_cnt value =%d\n", pcs_cnt);
+		break;
+	}
+
+	return ret;
+}
+static DEVICE_ATTR_RW(pcs_reg);
+
+static ssize_t phy_reg_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	int val = 0;
+	int err = -EINVAL;
+
+	if (hw) {
+		if (is_phy_ext_reg)
+			err = rnpm_mbx_phy_read(hw, phy_reg | PHY_EXT_REG_FLAG,
+						&val);
+		else
+			err = rnpm_mbx_phy_read(hw, phy_reg, &val);
+	}
+
+	if (err)
+		return 0;
+	else
+		return sprintf(buf, "phy %s 0x%04x : 0x%04x\n",
+			       is_phy_ext_reg ? "ext reg" : "reg", phy_reg,
+			       val & 0xffff);
+}
+
+static ssize_t phy_reg_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	int i = 0, argc = 0, err = -EINVAL;
+	char argv[3][16];
+	unsigned long val[3] = { 0 };
+
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+
+	memset(argv, 0, sizeof(argv));
+	argc = sscanf(buf, "%15s %15s %15s", argv[0], argv[1], argv[2]);
+
+	if (argc < 1)
+		return -EINVAL;
+
+	is_phy_ext_reg = 0;
+
+	if (strcmp(argv[0], "ext") == 0) {
+		is_phy_ext_reg = 1;
+	} else {
+		if (kstrtoul(argv[0], 0, &val[0]))
+			return -EINVAL;
+	}
+
+	for (i = 1; i < argc; i++) {
+		if (kstrtoul(argv[i], 0, &val[i]))
+			return -EINVAL;
+	}
+
+	if (argc == 1) {
+		if (is_phy_ext_reg)
+			return -EINVAL;
+		/* set phy reg index */
+		phy_reg = val[0];
+		err = 0;
+	}
+
+	if (argc == 2) {
+		if (is_phy_ext_reg) {
+			/* set ext phy reg index */
+			phy_reg = val[1];
+			err = 0;
+		} else {
+			/* write phy reg */
+			phy_reg = val[0];
+			err = rnpm_mbx_phy_write(hw, phy_reg, val[1]);
+		}
+	}
+
+	if (argc == 3) {
+		if (is_phy_ext_reg) {
+			/* write ext phy reg */
+			phy_reg = val[1];
+			err = rnpm_mbx_phy_write(hw, phy_reg | PHY_EXT_REG_FLAG,
+						 val[2]);
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	return err ? err : count;
+}
+static DEVICE_ATTR_RW(phy_reg);
+
+static ssize_t pma_rx2tx_loopback_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	int v, en_loopback, rc;
+	int nr_lane = adapter->lane % 4;
+
+	rc = kstrtou32(buf, 0, &en_loopback);
+	if (rc)
+		return -EINVAL;
+	e_dev_info("%s: nr_lane:%d lp:%d\n", __func__, nr_lane, en_loopback);
+
+	if (!hw->pcs.ops.write || !hw->pcs.ops.read)
+		return -EOPNOTSUPP;
+
+	// pma-rx2tx_loopback
+	v = hw->pcs.ops.read(hw, nr_lane, 0x18090);
+	if (en_loopback)
+		v |= 0xf << 4;
+	else
+		v &= ~(0xf << 4);
+	hw->pcs.ops.write(hw, nr_lane, 0x18090, v);
+
+	// mac tx-disable
+	v = rd32(hw, RNPM_MAC_TX_CFG(nr_lane));
+	if (en_loopback)
+		v &= ~(BIT(0)); // disable mac-tx
+	else
+		v |= (BIT(0)); // enable mac-tx
+
+	wr32(hw, RNPM_MAC_TX_CFG(nr_lane), v);
+
+	// mac rx-disable
+	v = rd32(hw, RNPM_MAC_RX_CFG(nr_lane));
+	if (en_loopback)
+		v &= ~(BIT(0)); // disable mac-rx
+	else
+		v |= (BIT(0)); // enable mac-rx
+	wr32(hw, RNPM_MAC_RX_CFG(nr_lane), v);
+
+	return count;
+}
+static DEVICE_ATTR_WO(pma_rx2tx_loopback);
+
+static ssize_t pcs_rx2tx_loopback_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct net_device *netdev = to_net_device(dev);
+	struct rnpm_adapter *adapter = netdev_priv(netdev);
+	struct rnpm_hw *hw = &adapter->hw;
+	int v, en_loopback, rc;
+	int nr_lane = adapter->lane % 4;
+
+	rc = kstrtou32(buf, 0, &en_loopback);
+	if (rc)
+		return -EINVAL;
+	e_dev_info("%s: nr_lane:%d lp:%d\n", __func__, nr_lane, en_loopback);
+
+	if (!hw->pcs.ops.write || !hw->pcs.ops.read)
+		return -EOPNOTSUPP;
+
+	// pma-rx2tx_loopback
+	v = hw->pcs.ops.read(hw, nr_lane, 0x38000);
+	if (en_loopback)
+		v |= BIT(14); // RX2TX_LB_EN
+	else
+		v &= ~BIT(14); // RX2TX_LB_EN
+	hw->pcs.ops.write(hw, nr_lane, 0x38000, v);
+
+	// mac tx-disable
+	v = rd32(hw, RNPM_MAC_TX_CFG(nr_lane));
+	if (en_loopback)
+		v &= ~(BIT(0)); // disable mac-tx
+	else
+		v |= (BIT(0)); // enable mac-tx
+	wr32(hw, RNPM_MAC_TX_CFG(nr_lane), v);
+
+	// mac rx-disable
+	v = rd32(hw, RNPM_MAC_RX_CFG(nr_lane));
+	if (en_loopback)
+		v &= ~(BIT(0)); // disable mac-rx
+	else
+		v |= (BIT(0)); // enable mac-rx
+	wr32(hw, RNPM_MAC_RX_CFG(nr_lane), v);
+
+	return count;
+}
+static DEVICE_ATTR_WO(pcs_rx2tx_loopback);
+
+static int do_switch_loopback_set(struct rnpm_adapter *adapter, int en,
+				  int sport_lane, int dst_switch_port)
+{
+	int v;
+	struct rnpm_hw *hw = &adapter->hw;
+
+	e_dev_info("%s: %s %d -> %d en:%d\n", __func__,
+		   netdev_name(adapter->netdev), sport_lane, dst_switch_port,
+		   en);
+
+	if (en)
+		adapter->flags |= RNPM_FLAG_SWITCH_LOOPBACK_EN;
+	else
+		adapter->flags &= ~RNPM_FLAG_SWITCH_LOOPBACK_EN;
+
+	// redir pkgs to peer
+	wr32(hw, RNPM_ETH_INPORT_POLICY_REG(sport_lane),
+	     BIT(29) | (dst_switch_port << 16));
+
+	// enable/disable policy
+	v = rd32(hw, RNPM_ETH_INPORT_POLICY_VAL);
+	if (en)
+		v |= BIT(sport_lane); // enable this-port-policy
+	else
+		v &= ~BIT(sport_lane);
+	wr32(hw, RNPM_ETH_INPORT_POLICY_VAL, v);
+
+	// mac promisc
+	v = rd32(hw, RNPM_MAC_PKT_FLT(sport_lane));
+	if (en)
+		v |= (RNPM_RX_ALL | RNPM_RX_ALL_MUL);
+	else
+		v &= ~(RNPM_RX_ALL | RNPM_RX_ALL_MUL);
+	wr32(hw, RNPM_MAC_PKT_FLT(sport_lane), v);
+
+	return 0;
+}
+
+static int to_switch_port(struct rnpm_adapter *adapter)
+{
+	int switch_port = adapter->lane;
+
+	if (rnpm_is_pf1(adapter->pdev))
+		switch_port += 4;
+
+	if (adapter->hw.mode == MODE_NIC_MODE_2PORT) {
+		if (adapter->lane != 0)
+			switch_port += 1;
+	}
+
+	return switch_port;
+}
+
+static ssize_t _switch_loopback(struct rnpm_adapter *adapter,
+				const char *peer_eth, int en)
+{
+	struct net_device *peer_netdev = NULL;
+	struct rnpm_adapter *peer_adapter = NULL;
+	int i;
+	char name[100];
+
+	strscpy(name, peer_eth, sizeof(name));
+	strim(name);
+
+	peer_netdev = dev_get_by_name(&init_net, name);
+	if (!peer_netdev) {
+		e_dev_info("canot' find [%s]\n", name);
+		return -EINVAL;
+	}
+	peer_adapter = netdev_priv(peer_netdev);
+
+	// check if in same slot
+	if (PCI_SLOT(peer_adapter->pdev->devfn) !=
+	    PCI_SLOT(adapter->pdev->devfn)) {
+		e_dev_info("%s %s not in same slot\n",
+			   netdev_name(adapter->netdev),
+			   netdev_name(peer_adapter->netdev));
+		dev_put(peer_netdev);
+		return -EINVAL;
+	}
+
+	e_dev_info("%s: %s(%d,%d) <-> %s(%d,%d) en:%d\n", __func__,
+		   netdev_name(adapter->netdev), adapter->lane, adapter->port,
+		   netdev_name(peer_adapter->netdev), peer_adapter->lane,
+		   peer_adapter->port, en);
+
+	/* clear pf0/pf1 input port policy eth reg */
+	for (i = 0; i < MAX_PORT_NUM; i++) {
+		wr32(&adapter->hw, RNPM_ETH_INPORT_POLICY_REG(i), 0);
+		wr32(&peer_adapter->hw, RNPM_ETH_INPORT_POLICY_REG(i), 0);
+	}
+
+	wr32(&adapter->hw, RNPM_ETH_INPORT_POLICY_VAL, 0);
+	wr32(&peer_adapter->hw, RNPM_ETH_INPORT_POLICY_VAL, 0);
+
+	do_switch_loopback_set(adapter, en, adapter->lane,
+			       to_switch_port(peer_adapter));
+
+	do_switch_loopback_set(peer_adapter, en, peer_adapter->lane,
+			       to_switch_port(adapter));
+
+	if (peer_netdev)
+		dev_put(peer_netdev);
+
+	return 0;
+}
+static ssize_t switch_loopback_on_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct rnpm_adapter *adapter = netdev_priv(to_net_device(dev));
+
+	return _switch_loopback(adapter, buf, 1) == 0 ? count : -EINVAL;
+}
+static DEVICE_ATTR_WO(switch_loopback_on);
+
+static ssize_t switch_loopback_off_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rnpm_adapter *adapter = netdev_priv(to_net_device(dev));
+
+	return _switch_loopback(adapter, buf, 0) == 0 ? count : -EINVAL;
+}
+static DEVICE_ATTR_WO(switch_loopback_off);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_root_slot_info.attr,
+	&dev_attr_active_vid.attr,
+	&dev_attr_pf_reset.attr,
+	&dev_attr_ring_num.attr,
+	&dev_attr_port_idx.attr,
+	&dev_attr_own_vpd.attr,
+	&dev_attr_nr_lane.attr,
+	&dev_attr_temperature.attr,
+	&dev_attr_si.attr,
+	&dev_attr_sfp.attr,
+	&dev_attr_autoneg.attr,
+	&dev_attr_sfp_tx_disable.attr,
+	&dev_attr_fec.attr,
+	&dev_attr_link_traing.attr,
+	&dev_attr_pci.attr,
+	&dev_attr_prbs.attr,
+	&dev_attr_debug_linkstat.attr,
+	&dev_attr_debug_aptstat.attr,
+	&dev_attr_tx_counter.attr,
+	&dev_attr_rx_counter.attr,
+	&dev_attr_bar4_reg.attr,
+	&dev_attr_phy_statistics.attr,
+	&dev_attr_pcs_reg.attr,
+	&dev_attr_phy_reg.attr,
+	&dev_attr_pma_rx2tx_loopback.attr,
+	&dev_attr_pcs_rx2tx_loopback.attr,
+	&dev_attr_switch_loopback_off.attr,
+	&dev_attr_switch_loopback_on.attr,
+	NULL,
+};
+
+#ifndef NO_BIT_ATTRS
+static struct bin_attribute *dev_bin_attrs[] = {
+	&bin_attr_maintain,
+	NULL,
+};
+#endif
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+#ifndef NO_BIT_ATTRS
+	.bin_attrs = dev_bin_attrs,
+#endif
+};
+
+/* hwmon callback functions */
+static ssize_t rnpm_hwmon_show_location(struct device __always_unused *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct hwmon_attr *rnpm_attr =
+		container_of(attr, struct hwmon_attr, dev_attr);
+
+	return snprintf(buf, PAGE_SIZE, "loc%u\n", rnpm_attr->sensor->location);
+}
+
+static ssize_t rnpm_hwmon_show_name(struct device __always_unused *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "rnpm\n");
+}
+
+static ssize_t rnpm_hwmon_show_temp(struct device __always_unused *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct hwmon_attr *rnpm_attr =
+		container_of(attr, struct hwmon_attr, dev_attr);
+	unsigned int value;
+
+	/* reset the temp field */
+	rnpm_attr->hw->mac.ops.get_thermal_sensor_data(rnpm_attr->hw);
+
+	value = rnpm_attr->sensor->temp;
+	/* display millidegree */
+	value *= 1000;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t rnpm_hwmon_show_cautionthresh(struct device __always_unused *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct hwmon_attr *rnpm_attr =
+		container_of(attr, struct hwmon_attr, dev_attr);
+	unsigned int value = rnpm_attr->sensor->caution_thresh;
+	/* display millidegree */
+	value *= 1000;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t rnpm_hwmon_show_maxopthresh(struct device __always_unused *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct hwmon_attr *rnpm_attr =
+		container_of(attr, struct hwmon_attr, dev_attr);
+	unsigned int value = rnpm_attr->sensor->max_op_thresh;
+
+	/* display millidegree */
+	value *= 1000;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+/**
+ * rnpm_add_hwmon_attr - Create hwmon attr table for a hwmon sysfs file.
+ * @adapter: pointer to the adapter structure
+ * @offset: offset in the eeprom sensor data table
+ * @type: type of sensor data to display
+ *
+ * For each file we want in hwmon's sysfs interface we need a device_attribute
+ * This is included in our hwmon_attr struct that contains the references to
+ * the data structures we need to get the data to display.
+ */
+static int rnpm_add_hwmon_attr(struct rnpm_adapter *adapter,
+			       unsigned int offset, int type)
+{
+	unsigned int n_attr;
+	struct hwmon_attr *rnpm_attr;
+
+	n_attr = adapter->rnpm_hwmon_buff->n_hwmon;
+	rnpm_attr = &adapter->rnpm_hwmon_buff->hwmon_list[n_attr];
+
+	switch (type) {
+	case RNPM_HWMON_TYPE_LOC:
+		rnpm_attr->dev_attr.show = rnpm_hwmon_show_location;
+		snprintf(rnpm_attr->name, sizeof(rnpm_attr->name),
+			 "temp%u_label", offset + 1);
+
+		break;
+	case RNPM_HWMON_TYPE_NAME:
+		rnpm_attr->dev_attr.show = rnpm_hwmon_show_name;
+		snprintf(rnpm_attr->name, sizeof(rnpm_attr->name), "name");
+
+		break;
+	case RNPM_HWMON_TYPE_TEMP:
+		rnpm_attr->dev_attr.show = rnpm_hwmon_show_temp;
+		snprintf(rnpm_attr->name, sizeof(rnpm_attr->name),
+			 "temp%u_input", offset + 1);
+
+		break;
+	case RNPM_HWMON_TYPE_CAUTION:
+		rnpm_attr->dev_attr.show = rnpm_hwmon_show_cautionthresh;
+		snprintf(rnpm_attr->name, sizeof(rnpm_attr->name), "temp%u_max",
+			 offset + 1);
+
+		break;
+	case RNPM_HWMON_TYPE_MAX:
+		rnpm_attr->dev_attr.show = rnpm_hwmon_show_maxopthresh;
+		snprintf(rnpm_attr->name, sizeof(rnpm_attr->name),
+			 "temp%u_crit", offset + 1);
+
+		break;
+	default:
+		return -EPERM;
+	}
+
+	/* These always the same regardless of type */
+	rnpm_attr->sensor = &adapter->hw.mac.thermal_sensor_data.sensor[offset];
+	rnpm_attr->hw = &adapter->hw;
+	rnpm_attr->dev_attr.store = NULL;
+	rnpm_attr->dev_attr.attr.mode = 0444;
+	rnpm_attr->dev_attr.attr.name = rnpm_attr->name;
+
+	sysfs_attr_init(&rnpm_attr->dev_attr.attr);
+
+	adapter->rnpm_hwmon_buff->attrs[n_attr] = &rnpm_attr->dev_attr.attr;
+
+	++adapter->rnpm_hwmon_buff->n_hwmon;
+
+	return 0;
+}
+
+/* called from rnpm_main.c */
+void rnpm_sysfs_exit(struct rnpm_adapter *adapter)
+{
+	sysfs_remove_group(&adapter->netdev->dev.kobj, &dev_attr_grp);
+}
+
+/* called from rnpm_main.c */
+int rnpm_sysfs_init(struct rnpm_adapter *adapter, int port)
+{
+	int err, rc = 0;
+	struct hwmon_buff *rnpm_hwmon;
+	struct device *hwmon_dev;
+	unsigned int i;
+
+	err = sysfs_create_group(&adapter->netdev->dev.kobj, &dev_attr_grp);
+	if (err != 0) {
+		dev_err(&adapter->netdev->dev,
+			"sysfs_create_group failed:err:%d\n", err);
+		return err;
+	}
+	/* only  port0 and pcie devfn all both 0 register temperature hwmon */
+	if (!!port || !!adapter->pdev->devfn)
+		goto exit;
+
+	/* If this method isn't defined we don't support thermals */
+	if (adapter->hw.mac.ops.init_thermal_sensor_thresh == NULL)
+		goto no_thermal;
+
+	/* Don't create thermal hwmon interface if no sensors present */
+	if (adapter->hw.mac.ops.init_thermal_sensor_thresh(&adapter->hw))
+		goto no_thermal;
+
+	rnpm_hwmon = devm_kzalloc(&adapter->pdev->dev, sizeof(*rnpm_hwmon),
+				  GFP_KERNEL);
+
+	if (!rnpm_hwmon) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	adapter->rnpm_hwmon_buff = rnpm_hwmon;
+	/* Only support one sensor now */
+	for (i = 0; i < RNPM_MAX_SENSORS; i++) {
+		/* Only create hwmon sysfs entries for sensors that have
+		 * meaningful data for.
+		 */
+		if (adapter->hw.mac.thermal_sensor_data.sensor[i].location == 0)
+			continue;
+
+		/* Bail if any hwmon attr struct fails to initialize */
+		rc = rnpm_add_hwmon_attr(adapter, i, RNPM_HWMON_TYPE_CAUTION);
+		if (rc)
+			goto err;
+		rc = rnpm_add_hwmon_attr(adapter, i, RNPM_HWMON_TYPE_TEMP);
+		if (rc)
+			goto err;
+		rc = rnpm_add_hwmon_attr(adapter, i, RNPM_HWMON_TYPE_MAX);
+		if (rc)
+			goto err;
+	}
+
+	rnpm_hwmon->groups[0] = &rnpm_hwmon->group;
+	rnpm_hwmon->group.attrs = rnpm_hwmon->attrs;
+
+	hwmon_dev = devm_hwmon_device_register_with_groups(
+		&adapter->pdev->dev, "rnpm", rnpm_hwmon, rnpm_hwmon->groups);
+
+	if (IS_ERR(hwmon_dev)) {
+		rc = PTR_ERR(hwmon_dev);
+		goto exit;
+	}
+no_thermal:
+	goto exit;
+err:
+exit:
+	return rc;
+}
diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_tc.c b/drivers/net/ethernet/mucse/rnpm/rnpm_tc.c
new file mode 100644
index 000000000000..4457cda39ce9
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpm/rnpm_tc.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 - 2024 Mucse Corporation. */
+
+#include 
+#ifdef NETIF_F_HW_TC
+#include 
+#include 
+#include 
+#endif
+
+#include "rnpm_tc_u32_parse.h"
+#include "rnpm_tc.h"
+
+static void __maybe_unused rnpm_setup_txr_prio(void __iomem *ioaddr,
+					       struct rnpm_ring *tx_ring,
+					       int prio)
+{
+	u16 dma_ring_idx = tx_ring->rnpm_queue_idx;
+
+	rnpm_wr_reg(ioaddr + RNPM_DMA_REG_TX_ARB_DEF_LVL(dma_ring_idx), prio);
+}
+
+int rnpm_setup_tx_maxrate(void __iomem *ioaddr, struct rnpm_ring *tx_ring,
+			  u64 max_rate, int samples_1sec)
+{
+	u16 dma_ring_idx = tx_ring->rnpm_queue_idx;
+
+	/* set hardware samping internal 1S */
+	rnpm_wr_reg(ioaddr + RNPM_DMA_REG_TX_FLOW_CTRL_TM(dma_ring_idx),
+		    samples_1sec);
+	rnpm_wr_reg(ioaddr + RNPM_DMA_REG_TX_FLOW_CTRL_TH(dma_ring_idx),
+		    max_rate);
+	return 0;
+}
diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_tc.h b/drivers/net/ethernet/mucse/rnpm/rnpm_tc.h
new file mode 100644
index 000000000000..c640ba98e8e5
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpm/rnpm_tc.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2022 - 2024 Mucse Corporation. */
+
+#ifndef __RNPM_TC_H__
+#define __RNPM_TC_H__
+#include "rnpm.h"
+
+//#include 
+//int rnpm_setup_mqprio(struct net_device *netdev, void *type_data);
+
+//#if (defined HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SETUP_TC) || (defined NETIF_F_HW_TC)
+//#ifdef HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV
+//int rnpm_setup_tc_cls_u32(struct net_device *dev,
+//	struct tc_cls_u32_offload *cls_u32);
+//#else
+//int rnpm_setup_tc_cls_u32(struct net_device *dev, __be16 proto,
+//	struct tc_cls_u32_offload *cls_u32);
+//#endif
+//#if defined(HAVE_TCF_BLOCK)
+//#if defined(HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV)
+//int rnpm_setup_tc_block(struct net_device *dev,
+//	struct flow_block_offload *f);
+//
+//#endif
+//#endif
+//#endif
+#endif
diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_tc_u32_parse.h b/drivers/net/ethernet/mucse/rnpm/rnpm_tc_u32_parse.h
new file mode 100644
index 000000000000..15929c4c3e8a
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpm/rnpm_tc_u32_parse.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2022 - 2024 Mucse Corporation. */
+
+#ifndef __RNPM_TC_U32_PARSE_H__
+#define __RNPM_TC_U32_PARSE_H__
+#include "rnpm.h"
+
+struct rnpm_match_parser {
+	int off; /* the skb offset begin form the 12 bytes mac_type */
+	/* parse the value/mask to realy value*/
+	int (*val)(struct rnpm_fdir_filter *f, __be32 val, __be32 mask);
+};
+inline void ip_print(u32 ip, bool src_true)
+{
+	dbg("%s_ip is %d.%d.%d.%d\n", src_true ? "src" : "dst", ip & 0xff,
+	    ip >> 8 & 0xff, ip >> 16 & 0xff, ip >> 24 & 0xff);
+}
+/* Ipv4 Rule Parse */
+static inline int rnpm_fill_ipv4_src_ip(struct rnpm_fdir_filter *f, __be32 val,
+					__be32 mask)
+{
+	memcpy(&f->filter.formatted.src_ip[0], &val, sizeof(u32));
+	memcpy(&f->filter.formatted.src_ip_mask[0], &mask, sizeof(u32));
+
+	f->filter.formatted.flow_type = RNPM_ATR_FLOW_TYPE_IPV4;
+	f->filter.layer2_formate.proto = htons(ETH_P_IP);
+
+	ip_print(f->filter.formatted.src_ip[0], true);
+	dbg("ip mask is 0x%.2x\n", f->filter.formatted.src_ip_mask[0]);
+	return 0;
+}
+
+static inline int rnpm_fill_ipv4_dst_ip(struct rnpm_fdir_filter *f, __be32 val,
+					__be32 mask)
+{
+	memcpy(&f->filter.formatted.dst_ip[0], &val, sizeof(u32));
+	memcpy(&f->filter.formatted.dst_ip_mask[0], &mask, sizeof(u32));
+
+	f->filter.formatted.flow_type = RNPM_ATR_FLOW_TYPE_IPV4;
+	f->filter.layer2_formate.proto = htons(ETH_P_IP);
+
+	ip_print(f->filter.formatted.dst_ip[0], false);
+	dbg("ip mask is 0x%.2x\n", f->filter.formatted.dst_ip_mask[0]);
+
+	return 0;
+}
+
+static const struct rnpm_match_parser rnpm_ipv4_parser[] = {
+	{ .off = 12, .val = rnpm_fill_ipv4_src_ip },
+	{ .off = 16, .val = rnpm_fill_ipv4_dst_ip },
+	{ .val = NULL }
+};
+
+#endif
diff --git a/drivers/net/ethernet/mucse/rnpm/rnpm_type.h b/drivers/net/ethernet/mucse/rnpm/rnpm_type.h
new file mode 100644
index 000000000000..2daa30210b5e
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpm/rnpm_type.h
@@ -0,0 +1,1072 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2022 - 2024 Mucse Corporation. */
+
+#ifndef _RNPM_TYPE_H_
+#define _RNPM_TYPE_H_
+
+#include 
+#include 
+#include 
+
+#if IS_ENABLED(CONFIG_MXGBEM_OPTM_WITH_LPAGE)
+#ifndef RNPM_OPTM_WITH_LPAGE
+#define RNPM_OPTM_WITH_LPAGE
+#endif
+#endif
+#if (PAGE_SIZE < 8192)
+#ifdef RNPM_OPTM_WITH_LPAGE
+#undef RNPM_OPTM_WITH_LPAGE
+#endif
+#endif
+
+#if IS_ENABLED(CONFIG_MXGBEM_FIX_MAC_PADDING)
+#define RNPM_FIX_MAC_PADDING
+#endif
+
+#include "rnpm_regs.h"
+
+/* Device IDs */
+#define PCI_VENDOR_ID_MUCSE 0x8848
+#define RNPM_DEV_ID_N10_PF0 0x7001
+#define RNPM_DEV_ID_N10_PF1 0x7002
+
+#define RNPM_DEV_ID_N10_PF0_N 0x1000
+#define RNPM_DEV_ID_N10_PF1_N 0x1001
+
+/* Wake Up Control */
+#define RNPM_WUC_PME_EN 0x00000002 /* PME Enable */
+#define RNPM_WUC_PME_STATUS 0x00000004 /* PME Status */
+#define RNPM_WUC_WKEN 0x00000010 /* Enable PE_WAKE_N pin assertion  */
+
+/* Wake Up Filter Control */
+#define RNPM_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define RNPM_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define RNPM_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define RNPM_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */
+#define RNPM_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+#define RNPM_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */
+#define RNPM_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */
+#define RNPM_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */
+#define RNPM_WUFC_MNG 0x00000100 /* Directed Mgmt Packet Wakeup Enable */
+
+#define RNPM_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */
+#define RNPM_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */
+#define RNPM_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */
+#define RNPM_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */
+#define RNPM_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */
+#define RNPM_WUFC_FLX4 0x00100000 /* Flexible Filter 4 Enable */
+#define RNPM_WUFC_FLX5 0x00200000 /* Flexible Filter 5 Enable */
+#define RNPM_WUFC_FLX_FILTERS 0x000F0000 /* Mask for 4 flex filters */
+#define RNPM_WUFC_FLX_FILTERS_6 0x003F0000 /* Mask for 6 flex filters */
+#define RNPM_WUFC_FLX_FILTERS_8 0x00FF0000 /* Mask for 8 flex filters */
+#define RNPM_WUFC_FW_RST_WK 0x80000000 /* Ena wake on FW reset assertion */
+/* Mask for Ext. flex filters */
+#define RNPM_WUFC_EXT_FLX_FILTERS 0x00300000
+#define RNPM_WUFC_ALL_FILTERS 0x000F00FF /* Mask all 4 flex filters */
+#define RNPM_WUFC_ALL_FILTERS_6 0x003F00FF /* Mask all 6 flex filters */
+#define RNPM_WUFC_ALL_FILTERS_8 0x00FF00FF /* Mask all 8 flex filters */
+#define RNPM_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */
+
+#define RNPM_MAX_SENSORS 1
+struct rnpm_thermal_diode_data {
+	u8 location;
+	u8 temp;
+	u8 caution_thresh;
+	u8 max_op_thresh;
+};
+
+struct rnpm_thermal_sensor_data {
+	struct rnpm_thermal_diode_data sensor[RNPM_MAX_SENSORS];
+};
+
+/* Wake Up Status */
+#define RNPM_WUS_LNKC RNPM_WUFC_LNKC
+#define RNPM_WUS_MAG RNPM_WUFC_MAG
+#define RNPM_WUS_EX RNPM_WUFC_EX
+#define RNPM_WUS_MC RNPM_WUFC_MC
+#define RNPM_WUS_BC RNPM_WUFC_BC
+#define RNPM_WUS_ARP RNPM_WUFC_ARP
+#define RNPM_WUS_IPV4 RNPM_WUFC_IPV4
+#define RNPM_WUS_IPV6 RNPM_WUFC_IPV6
+#define RNPM_WUS_MNG RNPM_WUFC_MNG
+#define RNPM_WUS_FLX0 RNPM_WUFC_FLX0
+#define RNPM_WUS_FLX1 RNPM_WUFC_FLX1
+#define RNPM_WUS_FLX2 RNPM_WUFC_FLX2
+#define RNPM_WUS_FLX3 RNPM_WUFC_FLX3
+#define RNPM_WUS_FLX4 RNPM_WUFC_FLX4
+#define RNPM_WUS_FLX5 RNPM_WUFC_FLX5
+#define RNPM_WUS_FLX_FILTERS RNPM_WUFC_FLX_FILTERS
+#define RNPM_WUS_FW_RST_WK RNPM_WUFC_FW_RST_WK
+/* Proxy Status */
+#define RNPM_PROXYS_EX 0x00000004 /* Exact packet received */
+#define RNPM_PROXYS_ARP_DIR 0x00000020 /* ARP w/filter match received */
+#define RNPM_PROXYS_NS 0x00000200 /* IPV6 NS received */
+#define RNPM_PROXYS_NS_DIR 0x00000400 /* IPV6 NS w/DA match received */
+#define RNPM_PROXYS_ARP 0x00000800 /* ARP request packet received */
+#define RNPM_PROXYS_MLD 0x00001000 /* IPv6 MLD packet received */
+
+/* Proxying Filter Control */
+#define RNPM_PROXYFC_ENABLE 0x00000001 /* Port Proxying Enable */
+#define RNPM_PROXYFC_EX 0x00000004 /* Directed Exact Proxy Enable */
+#define RNPM_PROXYFC_ARP_DIR 0x00000020 /* Directed ARP Proxy Enable */
+#define RNPM_PROXYFC_NS 0x00000200 /* IPv6 Neighbor Solicitation */
+#define RNPM_PROXYFC_ARP 0x00000800 /* ARP Request Proxy Enable */
+#define RNPM_PROXYFC_MLD 0x00000800 /* IPv6 MLD Proxy Enable */
+#define RNPM_PROXYFC_NO_TCO 0x00008000 /* Ignore TCO packets */
+
+#define RNPM_WUPL_LENGTH_MASK 0xFFFF
+
+#define RNPM_MAX_TRAFFIC_CLASS 4
+#define TSRN10_TX_DEFAULT_BURST 8
+
+#ifndef TSRN10_RX_DEFAULT_LINE
+#define TSRN10_RX_DEFAULT_LINE 64
+#endif
+#ifndef TSRN10_RX_DEFAULT_BURST
+#define TSRN10_RX_DEFAULT_BURST 16
+#endif
+#define RNPM_TX_PKT_POLL_BUDGET 128
+
+#ifndef RNPM_RX_PKT_POLL_BUDGET
+#define RNPM_RX_PKT_POLL_BUDGET 64
+#endif
+
+#ifndef RNPM_PKT_TIMEOUT
+#define RNPM_PKT_TIMEOUT 30
+#endif
+
+#ifndef RNPM_PKT_TIMEOUT_TX
+#define RNPM_PKT_TIMEOUT_TX 100
+#endif
+
+/* VF Device IDs */
+#define RNPM_DEV_ID_N10_PF0_VF 0x8001
+#define RNPM_DEV_ID_N10_PF1_VF 0x8002
+
+#define RNPM_DEV_ID_N10_PF0_VF_N 0x1010
+#define RNPM_DEV_ID_N10_PF1_VF_N 0x1011
+
+/* Transmit Descriptor - Advanced */
+struct rnpm_tx_desc {
+	union {
+		__le64 pkt_addr; /* Packet buffer address */
+		struct {
+			__le32 adr_lo;
+			__le32 adr_hi;
+		};
+	};
+	__le32 blen_mac_ip_len;
+	__le32 vlan_cmd;
+#define RNPM_TXD_FLAGS_VLAN_PRIO_MASK 0xe000
+#define RNPM_TX_FLAGS_VLAN_PRIO_SHIFT 13
+#define RNPM_TX_FLAGS_VLAN_CFI_SHIFT 12
+
+#define RNPM_TXD_VLAN_VALID (0x80000000)
+#define RNPM_TXD_VLAN_CTRL_NOP (0x00 << 13)
+#define RNPM_TXD_VLAN_CTRL_RM_VLAN (0x20000000)
+#define RNPM_TXD_VLAN_CTRL_INSERT_VLAN (0x40000000)
+
+#define RNPM_TXD_L4_CSUM (0x10000000) /* udp tcp sctp csum */
+#define RNPM_TXD_IP_CSUM (0x8000000)
+#define RNPM_TXD_TUNNEL_VXLAN (0x1000000)
+#define RNPM_TXD_TUNNEL_NVGRE (0x2000000)
+#define RNPM_TXD_L4_TYPE_UDP (0xc00000)
+#define RNPM_TXD_L4_TYPE_TCP (0x400000)
+#define RNPM_TXD_L4_TYPE_SCTP (0x800000)
+#define RNPM_TXD_FLAG_IPv4 (0)
+#define RNPM_TXD_FLAG_IPv6 (0x200000)
+#define RNPM_TXD_FLAG_TSO (0x100000)
+#define RNPM_TXD_FLAG_PTP (0x4000000)
+#define RNPM_TXD_CMD_RS (0x040000)
+#define RNPM_TXD_CMD_INNER_VLAN (0x08000000)
+#define RNPM_TXD_STAT_DD (0x020000)
+#define RNPM_TXD_CMD_EOP (0x010000)
+} __packed;
+
+struct rnpm_tx_ctx_desc {
+	__le32 mss_len_vf_num;
+	__le32 inner_vlan_tunnel_len;
+#define VF_VEB_MARK (1 << 24) // bit 56
+	__le32 resv;
+	__le32 resv_cmd;
+#define RNPM_TXD_FLAG_TO_RPU (0x80000000)
+#define RNPM_TXD_SMAC_CTRL_NOP (0x00 << 12)
+#define RNPM_TXD_SMAC_CTRL_REPLACE_MACADDR0 (0x02 << 12)
+#define RNPM_TXD_SMAC_CTRL_REPLACE_MACADDR1 (0x06 << 12)
+#define RNPM_TXD_CTX_VLAN_CTRL_NOP (0x00 << 10)
+#define RNPM_TXD_CTX_VLAN_CTRL_RM_VLAN (0x01 << 10)
+#define RNPM_TXD_CTX_VLAN_CTRL_INSERT_VLAN (0x02 << 10)
+#define RNPM_TXD_MTI_CRC_PAD_CTRL (0x01000000)
+#define RNPM_TXD_CTX_CTRL_DESC (0x080000)
+#define RNPM_TXD_CMD_RS (0x040000)
+#define RNPM_TXD_STAT_DD (0x020000)
+} __packed;
+
+/* Receive Descriptor - Advanced */
+union rnpm_rx_desc {
+	struct {
+		union {
+			__le64 pkt_addr; /* Packet buffer address */
+			struct {
+				__le32 addr_lo;
+				__le32 addr_hi;
+			};
+		};
+		__le64 resv_cmd;
+#define RNPM_RXD_FLAG_RS (0)
+	};
+
+	struct {
+		__le32 rss_hash;
+		__le16 mark;
+#define VEB_VF_PKG (1 << 15)
+		__le16 rev1;
+		__le16 len;
+		__le16 padding_len;
+		__le16 vlan;
+		__le16 cmd;
+#define RNPM_RX_L3_TYPE_MASK (1 << 15) // 1 is ipv4
+#define RNPM_RXD_STAT_L4_MASK (0x02 << 8)
+#define RNPM_RXD_STAT_VLAN_VALID (1 << 15)
+#define RNPM_RXD_STAT_TUNNEL_NVGRE (0x02 << 13)
+#define RNPM_RXD_STAT_TUNNEL_VXLAN (0x01 << 13)
+#define RNPM_RXD_STAT_TUNNEL_MASK (0x03 << 13)
+#define RNPM_RXD_STAT_ERR_MASK (0x1f << 8)
+#define RNPM_RXD_STAT_SCTP_MASK (0x04 << 8)
+#define RNPM_RXD_STAT_L4_SCTP (0x02 << 6)
+#define RNPM_RXD_STAT_L4_TCP (0x01 << 6)
+#define RNPM_RXD_STAT_L4_UDP (0x03 << 6)
+#define RNPM_RXD_STAT_IPV6 (1 << 5)
+#define RNPM_RXD_STAT_IPV4 (0 << 5)
+#define RNPM_RXD_STAT_PTP (1 << 4)
+#define RNPM_RXD_STAT_DD (1 << 1)
+#define RNPM_RXD_STAT_EOP (1 << 0)
+	} wb;
+} __packed;
+
+/* Host Interface Command Structures */
+struct rnpm_hic_hdr {
+	u8 cmd;
+	u8 buf_len;
+	union {
+		u8 cmd_resv;
+		u8 ret_status;
+	} cmd_or_resp;
+	u8 checksum;
+};
+
+struct rnpm_hic_drv_info {
+	struct rnpm_hic_hdr hdr;
+	u8 port_num;
+	u8 ver_sub;
+	u8 ver_build;
+	u8 ver_min;
+	u8 ver_maj;
+	u8 pad; /* end spacing to ensure length is mult. of dword */
+	u16 pad2; /* end spacing to ensure length is mult. of dword2 */
+};
+
+/* Context descriptors */
+struct rnpm_adv_tx_context_desc {
+	__le32 vlan_macip_lens;
+	__le32 seqnum_seed;
+	__le32 type_tucmd_mlhl;
+	__le32 mss_l4len_idx;
+};
+
+/* RAH */
+#define RNPM_RAH_VIND_MASK 0x003C0000
+#define RNPM_RAH_VIND_SHIFT 18
+#define RNPM_RAH_AV 0x80000000
+#define RNPM_CLEAR_VMDQ_ALL 0xFFFFFFFF
+
+/* Autonegotiation advertised speeds */
+typedef u32 rnpm_autoneg_advertised;
+/* Link speed */
+typedef u32 rnpm_link_speed;
+#define RNPM_LINK_SPEED_UNKNOWN 0
+#define RNPM_LINK_SPEED_10_FULL BIT(2)
+#define RNPM_LINK_SPEED_100_FULL BIT(3)
+#define RNPM_LINK_SPEED_1GB_FULL BIT(4)
+#define RNPM_LINK_SPEED_10GB_FULL BIT(5)
+#define RNPM_LINK_SPEED_40GB_FULL BIT(6)
+#define RNPM_LINK_SPEED_25GB_FULL BIT(7)
+#define RNPM_LINK_SPEED_50GB_FULL BIT(8)
+#define RNPM_LINK_SPEED_100GB_FULL BIT(9)
+#define RNPM_LINK_SPEED_10_HALF BIT(10)
+#define RNPM_LINK_SPEED_100_HALF BIT(11)
+#define RNPM_LINK_SPEED_1GB_HALF BIT(12)
+#define RNPM_SFP_MODE_10G_LR BIT(13)
+#define RNPM_SFP_MODE_10G_SR BIT(14)
+#define RNPM_SFP_MODE_10G_LRM BIT(15)
+#define RNPM_SFP_MODE_1G_T BIT(16)
+#define RNPM_SFP_MODE_1G_KX BIT(17)
+#define RNPM_SFP_MODE_1G_SX BIT(18)
+#define RNPM_SFP_MODE_1G_LX BIT(19)
+#define RNPM_SFP_MODE_40G_SR4 BIT(20)
+#define RNPM_SFP_MODE_40G_CR4 BIT(21)
+#define RNPM_SFP_MODE_40G_LR4 BIT(22)
+#define RNPM_SFP_MODE_1G_CX BIT(23)
+#define RNPM_SFP_MODE_10G_BASE_T BIT(24)
+#define RNPM_SFP_MODE_FIBER_CHANNEL_SPEED BIT(25) // sfp-a0-10 != 0
+#define RNPM_SFP_CONNECTOR_DAC BIT(26)
+#define RNPM_SFP_TO_SGMII BIT(27)
+
+#define RNPM_MODULE_QSFP_MAX_LEN 640
+
+/* PHY ID */
+#define RNPM_YT8614_PHY_ID 0x4f51e91a
+
+#define RNPM_LINK_SPEED_n10_AUTONEG                                            \
+	(RNPM_LINK_SPEED_10_FULL | RNPM_LINK_SPEED_100_FULL |                  \
+	 RNPM_LINK_SPEED_1GB_FULL | RNPM_LINK_SPEED_10GB_FULL)
+
+/* Flow Control Data Sheet defined values
+ * Calculation and defines taken from 802.1bb Annex O
+ */
+
+enum rnpm_atr_flow_type {
+	RNPM_ATR_FLOW_TYPE_IPV4 = 0x0,
+	RNPM_ATR_FLOW_TYPE_UDPV4 = 0x1,
+	RNPM_ATR_FLOW_TYPE_TCPV4 = 0x2,
+	RNPM_ATR_FLOW_TYPE_SCTPV4 = 0x3,
+	RNPM_ATR_FLOW_TYPE_IPV6 = 0x4,
+	RNPM_ATR_FLOW_TYPE_UDPV6 = 0x5,
+	RNPM_ATR_FLOW_TYPE_TCPV6 = 0x6,
+	RNPM_ATR_FLOW_TYPE_SCTPV6 = 0x7,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_IPV4 = 0x10,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_UDPV4 = 0x11,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_TCPV4 = 0x12,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_SCTPV4 = 0x13,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_IPV6 = 0x14,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_UDPV6 = 0x15,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_TCPV6 = 0x16,
+	RNPM_ATR_FLOW_TYPE_TUNNELED_SCTPV6 = 0x17,
+	RNPM_ATR_FLOW_TYPE_ETHER = 0x18,
+	RNPM_ATR_FLOW_TYPE_USERDEF = 0x19,
+};
+
+#define RNPM_FDIR_DROP_QUEUE (200)
+
+enum {
+	fdir_mode_tcam = 0,
+	fdir_mode_tuple5,
+};
+/* Flow Director ATR input struct. */
+union rnpm_atr_input {
+	/* Byte layout in order, all values with MSB first:
+	 * vm_pool      - 1 byte
+	 * flow_type    - 1 byte
+	 * vlan_id      - 2 bytes
+	 * src_ip       - 16 bytes
+	 * inner_mac    - 6 bytes
+	 * cloud_mode   - 2 bytes
+	 * tni_vni      - 4 bytes
+	 * dst_ip       - 16 bytes
+	 * src_port     - 2 bytes
+	 * dst_port     - 2 bytes
+	 * flex_bytes   - 2 bytes
+	 * bkt_hash     - 2 bytes
+	 */
+	struct {
+		u8 vm_pool;
+		u8 flow_type;
+		__be16 vlan_id;
+		__be32 dst_ip[4];
+		__be32 dst_ip_mask[4];
+		__be32 src_ip[4];
+		__be32 src_ip_mask[4];
+		u8 inner_mac[6];
+		u8 inner_mac_mask[6];
+		__be16 tunnel_type;
+		__be32 tni_vni;
+		__be16 src_port;
+		__be16 src_port_mask;
+		__be16 dst_port;
+		__be16 dst_port_mask;
+		__be16 flex_bytes;
+		__be16 bkt_hash;
+	} formatted;
+	struct {
+		u8 vm_poll;
+		u8 flow_type;
+		u16 vlan_id;
+		__be16 proto;
+		__be16 resv;
+		__be32 nouse[12];
+	} layer2_formate;
+	__be32 dword_stream[14];
+};
+
+/* BitTimes (BT) conversion */
+#define RNPM_BT2KB(BT) ((BT + (8 * 1024 - 1)) / (8 * 1024))
+#define RNPM_B2BT(BT) (BT * 8)
+
+/* Calculate Delay to respond to PFC */
+#define RNPM_PFC_D 672
+
+/* Calculate Cable Delay */
+#define RNPM_CABLE_DC 5556 /* Delay Copper */
+#define RNPM_CABLE_DO 5000 /* Delay Optical */
+
+/* Calculate Interface Delay X540 */
+#define RNPM_PHY_DC 25600 /* Delay 10G BASET */
+#define RNPM_MAC_DC 8192 /* Delay Copper XAUI interface */
+#define RNPM_XAUI_DC (2 * 2048) /* Delay Copper Phy */
+
+#define RNPM_ID_X540 (RNPM_MAC_DC + RNPM_XAUI_DC + RNPM_PHY_DC)
+
+/* Calculate Interface Delay 82598, n10 */
+#define RNPM_PHY_D 12800
+#define RNPM_MAC_D 4096
+#define RNPM_XAUI_D (2 * 1024)
+
+/* PHY MDI STANDARD CONFIG */
+#define RNPM_PHY_MAX_BASIC_REGISTER_NUM (0x16)
+#define RNPM_MDI_PHY_ID1_OFFSET 2
+#define RNPM_MDI_PHY_ID2_OFFSET 3
+#define RNPM_MDI_PHY_ID_MASK 0xFFFFFC00U
+#define RNPM_MDI_PHY_SPEED_SELECT1 0x0040
+#define RNPM_MDI_PHY_DUPLEX 0x0100
+#define RNPM_MDI_PHY_RESTART_AN 0x0200
+#define RNPM_MDI_PHY_ANE 0x1000
+#define RNPM_MDI_PHY_SPEED_SELECT0 0x2000
+#define RNPM_MDI_PHY_RESET 0x8000
+
+#define NGBE_PHY_RST_WAIT_PERIOD 50
+
+#define RNPM_ID (RNPM_MAC_D + RNPM_XAUI_D + RNPM_PHY_D)
+
+/* Calculate Delay incurred from higher layer */
+#define RNPM_HD 6144
+
+/* Calculate PCI Bus delay for low thresholds */
+#define RNPM_PCI_DELAY 10000
+
+/* Flow Director compressed ATR hash input struct */
+union rnpm_atr_hash_dword {
+	struct {
+		u8 vm_pool;
+		u8 flow_type;
+		__be16 vlan_id;
+	} formatted;
+	__be32 ip;
+	struct {
+		__be16 src;
+		__be16 dst;
+	} port;
+	__be16 flex_bytes;
+	__be32 dword;
+};
+
+enum rnpm_eeprom_type {
+	rnpm_eeprom_uninitialized = 0,
+	rnpm_eeprom_spi,
+	rnpm_flash,
+	rnpm_eeprom_none /* No NVM support */
+};
+
+enum rnpm_mac_type {
+	rnp_mac_unknown = -1,
+	rnpm_mac_4lane_40G,
+	rnpm_mac_1lane_10_1G,
+	rnpm_mac_2lanes,
+	rnpm_mac_4lanes,
+	rnpm_num_macs,
+};
+enum rnpm_rss_type {
+	rnpm_rss_uv440 = 0,
+	rnpm_rss_uv3p,
+	rnpm_rss_n10,
+};
+enum rnpm_phy_type {
+	rnpm_phy_unknown = 0,
+	rnpm_phy_none,
+	rnpm_phy_sfp,
+	rnpm_phy_sfp_unsupported,
+	rnpm_phy_sfp_unknown,
+	rnpm_phy_generic
+};
+
+enum rnpm_sfp_type {
+	rnpm_sfp_type_da_cu = 0,
+	rnpm_sfp_type_sr = 1,
+	rnpm_sfp_type_lr = 2,
+	rnpm_sfp_type_da_cu_core0 = 3,
+	rnpm_sfp_type_da_cu_core1 = 4,
+	rnpm_sfp_type_srlr_core0 = 5,
+	rnpm_sfp_type_srlr_core1 = 6,
+	rnpm_sfp_type_da_act_lmt_core0 = 7,
+	rnpm_sfp_type_da_act_lmt_core1 = 8,
+	rnpm_sfp_type_1g_cu_core0 = 9,
+	rnpm_sfp_type_1g_cu_core1 = 10,
+	rnpm_sfp_type_1g_sx_core0 = 11,
+	rnpm_sfp_type_1g_sx_core1 = 12,
+	rnpm_sfp_type_1g_lx_core0 = 13,
+	rnpm_sfp_type_1g_lx_core1 = 14,
+	rnpm_sfp_type_not_present = 0xFFFE,
+	rnpm_sfp_type_unknown = 0xFFFF
+};
+
+enum rnpm_media_type {
+	rnpm_media_type_unknown = 0,
+	rnpm_media_type_fiber,
+	rnpm_media_type_copper,
+	rnpm_media_type_backplane,
+	rnpm_media_type_cx4,
+	rnpm_media_type_da,
+	rnpm_media_type_virtual
+};
+
+/* Flow Control Settings */
+enum rnpm_fc_mode {
+	rnpm_fc_none = 0,
+	rnpm_fc_rx_pause,
+	rnpm_fc_tx_pause,
+	rnpm_fc_full,
+	rnpm_fc_default
+};
+
+struct rnpm_addr_filter_info {
+	u32 num_mc_addrs;
+	u32 rar_used_count;
+	u32 mta_in_use;
+	u32 overflow_promisc;
+	bool uc_set_promisc;
+	bool user_set_promisc;
+};
+
+/* Bus parameters */
+struct rnpm_bus_info {
+	u16 func;
+	u16 lan_id;
+};
+
+/* Flow control parameters */
+struct rnpm_fc_info {
+	u32 high_water[RNPM_MAX_TRAFFIC_CLASS]; /* Flow Control High-water */
+	u32 low_water[RNPM_MAX_TRAFFIC_CLASS]; /* Flow Control Low-water */
+	u16 pause_time; /* Flow Control Pause timer */
+	bool send_xon; /* Flow control send XON */
+	bool strict_ieee; /* Strict IEEE mode */
+	bool disable_fc_autoneg; /* Do not autonegotiate FC */
+	bool fc_was_autonegged; /* Is current_mode the result of autonegging? */
+	enum rnpm_fc_mode current_mode; /* FC mode in effect */
+	enum rnpm_fc_mode requested_mode; /* FC mode requested by caller */
+};
+
+/* Statistics counters collected by the MAC */
+struct rnpm_hw_stats {
+	u64 dma_to_eth;
+	u64 dma_to_switch;
+	u64 mac_to_mac;
+	u64 switch_to_switch;
+	u64 mac_to_dma;
+	u64 switch_to_dma;
+	u64 vlan_add_cnt;
+	u64 vlan_strip_cnt;
+	//=== error
+	u64 invalid_droped_packets;
+	u64 filter_dropped_packets;
+	//== drop ==
+	u64 host_l2_match_drop;
+	u64 redir_input_match_drop;
+	u64 redir_etype_match_drop;
+	u64 redir_tcp_syn_match_drop;
+	u64 redir_tuple5_match_drop;
+	u64 redir_tcam_match_drop;
+
+	u64 bmc_dropped_packets;
+	u64 switch_dropped_packets;
+	//=== rx
+	u64 dma_to_host;
+	//=== dma-tx ==
+	u64 port0_tx_packets;
+	u64 port1_tx_packets;
+	u64 port2_tx_packets;
+	u64 port3_tx_packets;
+	//=== emac 1to4 tx ==
+	u64 in0_tx_pkts;
+	u64 in1_tx_pkts;
+	u64 in2_tx_pkts;
+	u64 in3_tx_pkts;
+	//=== phy tx ==
+	u64 port0_to_phy_pkts;
+	u64 port1_to_phy_pkts;
+	u64 port2_to_phy_pkts;
+	u64 port3_to_phy_pkts;
+	//=== mac rx ===
+	u64 mac_rx_broadcast;
+	u64 mac_rx_multicast;
+	u64 mac_tx_pause_cnt;
+	u64 mac_rx_pause_cnt;
+};
+
+/* forward declaration */
+struct rnpm_hw;
+
+/* iterator type for walking multicast address lists */
+typedef u8 *(*rnpm_mc_addr_itr)(struct rnpm_hw *hw, u8 **mc_addr_ptr,
+				u32 *vmdq);
+
+struct rnpm_mac_operations {
+	s32 (*init_hw)(struct rnpm_hw *hw);
+	s32 (*reset_hw)(struct rnpm_hw *hw);
+	s32 (*start_hw)(struct rnpm_hw *hw);
+	s32 (*clear_hw_cntrs)(struct rnpm_hw *hw);
+	enum rnpm_media_type (*get_media_type)(struct rnpm_hw *hw);
+	u32 (*get_supported_physical_layer)(struct rnpm_hw *hw);
+	s32 (*get_mac_addr)(struct rnpm_hw *hw, u8 *mac_addr);
+	s32 (*get_device_caps)(struct rnpm_hw *hw, u16 *device_caps);
+	s32 (*get_wwn_prefix)(struct rnpm_hw *hw, u16 *wwnn_prefix,
+			      u16 *wwpn_prefix);
+	s32 (*stop_adapter)(struct rnpm_hw *hw);
+	s32 (*get_bus_info)(struct rnpm_hw *hw);
+	void (*set_lan_id)(struct rnpm_hw *hw);
+	s32 (*setup_sfp)(struct rnpm_hw *hw);
+	s32 (*disable_rx_buff)(struct rnpm_hw *hw);
+	s32 (*enable_rx_buff)(struct rnpm_hw *hw);
+	s32 (*enable_rx_dma)(struct rnpm_hw *hw, u32 regval);
+	s32 (*acquire_swfw_sync)(struct rnpm_hw *hw, u16 mask);
+	void (*release_swfw_sync)(struct rnpm_hw *hw, u16 mask);
+
+	/* Link */
+	void (*disable_tx_laser)(struct rnpm_hw *hw);
+	void (*enable_tx_laser)(struct rnpm_hw *hw);
+	void (*flap_tx_laser)(struct rnpm_hw *hw);
+	s32 (*setup_link)(struct rnpm_hw *hw, rnpm_link_speed speed,
+			  bool autoneg_wait_to_complete);
+	s32 (*check_link)(struct rnpm_hw *hw, rnpm_link_speed *speed,
+			  bool *link_up, bool link_up_wait_to_complete);
+	s32 (*get_link_capabilities)(struct rnpm_hw *hw, rnpm_link_speed *speed,
+				     bool *autoneg, u32 *media_type);
+
+	/* Packet Buffer Manipulation */
+	void (*set_rxpba)(struct rnpm_hw *hw, int num_pb, u32 headroom,
+			  int strategy);
+
+	/* LED */
+	s32 (*led_on)(struct rnpm_hw *hw, u32 index);
+	s32 (*led_off)(struct rnpm_hw *hw, u32 index);
+	s32 (*blink_led_start)(struct rnpm_hw *hw, u32 index);
+	s32 (*blink_led_stop)(struct rnpm_hw *hw, u32 index);
+
+	/* RAR, Multicast, VLAN */
+	s32 (*set_rar)(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq,
+		       u32 enable_addr);
+	s32 (*set_rar_mac)(struct rnpm_hw *hw, u32 index, u8 *addr, u32 vmdq,
+			   u32 port);
+	s32 (*clear_rar)(struct rnpm_hw *hw, u32 index);
+	s32 (*clear_rar_mac)(struct rnpm_hw *hw, u32 index, u32 port);
+	s32 (*set_vmdq)(struct rnpm_hw *hw, u32 rar, u32 vmdq);
+	s32 (*clear_vmdq)(struct rnpm_hw *hw, u32 rar, u32 vmdq);
+	s32 (*init_rx_addrs)(struct rnpm_hw *hw);
+	s32 (*update_mc_addr_list)(struct rnpm_hw *hw,
+				   struct net_device *netdev);
+	s32 (*enable_mc)(struct rnpm_hw *hw);
+	s32 (*disable_mc)(struct rnpm_hw *hw);
+	s32 (*clear_vfta)(struct rnpm_hw *hw);
+	s32 (*set_vfta)(struct rnpm_hw *hw, u32 vlan, u32 vind, bool vlan_on);
+	s32 (*set_vfta_mac)(struct rnpm_hw *hw, u32 vlan, u32 vind,
+			    bool vlan_on);
+	s32 (*init_uta_tables)(struct rnpm_hw *hw);
+	void (*set_mac_anti_spoofing)(struct rnpm_hw *hw, bool enable, int pf);
+	void (*set_vlan_anti_spoofing)(struct rnpm_hw *hw, bool enable, int vf);
+
+	/* Flow Control */
+	s32 (*fc_enable)(struct rnpm_hw *hw);
+	s32 (*setup_fc)(struct rnpm_hw *hw);
+	/* Manageability interface */
+	s32 (*set_fw_drv_ver)(struct rnpm_hw *hw, u8 maj, u8 min, u8 build,
+			      u8 sub);
+	s32 (*get_thermal_sensor_data)(struct rnpm_hw *hw);
+	s32 (*init_thermal_sensor_thresh)(struct rnpm_hw *hw);
+	bool (*mng_fw_enabled)(struct rnpm_hw *hw);
+};
+
+struct rnpm_phy_operations {
+	s32 (*identify)(struct rnpm_hw *hw);
+	s32 (*identify_sfp)(struct rnpm_hw *hw);
+	s32 (*init)(struct rnpm_hw *hw);
+	s32 (*reset)(struct rnpm_hw *hw);
+	s32 (*read_reg)(struct rnpm_hw *hw, u32 reg_addr, u32 device_type,
+			u16 *phy_data);
+	s32 (*write_reg)(struct rnpm_hw *hw, u32 reg_addr, u32 device_type,
+			 u16 phy_data);
+	s32 (*setup_link)(struct rnpm_hw *hw);
+	s32 (*setup_link_speed)(struct rnpm_hw *hw, rnpm_link_speed speed,
+				bool autoneg_wait_to_complete);
+	s32 (*read_i2c_byte)(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr,
+			     u8 *data);
+	s32 (*write_i2c_byte)(struct rnpm_hw *hw, u8 byte_offset, u8 dev_addr,
+			      u8 data);
+	s32 (*read_i2c_sff8472)(struct rnpm_hw *hw, u8 byte_offset,
+				u8 *sff8472_data);
+	s32 (*read_i2c_eeprom)(struct rnpm_hw *hw, u8 byte_offset,
+			       u8 *eeprom_data);
+	s32 (*write_i2c_eeprom)(struct rnpm_hw *hw, u8 byte_offset,
+				u8 eeprom_data);
+	s32 (*check_overtemp)(struct rnpm_hw *hw);
+};
+
+struct rnpm_eeprom_info {
+	enum rnpm_eeprom_type type;
+	u32 semaphore_delay;
+	u16 word_size;
+	u16 address_bits;
+	u16 word_page_size;
+};
+
+#define RNPM_FLAGS_DOUBLE_RESET_REQUIRED 0x01
+#define RNPM_FLAGS_INIT_MAC_ADDRESS 0x02
+
+enum mc_filter_type {
+	rnpm_mc_filter_type0, /* nic hash table mode 0 */
+	rnpm_mc_filter_type1, /* nic hash table mode 1 */
+	rnpm_mc_filter_type2, /* nic hash table mode 2 */
+	rnpm_mc_filter_type3, /* nic hash table mode 3 */
+	rnpm_mc_filter_type4, /* mac hash table mode */
+};
+
+enum mc_location_type {
+	rnpm_mc_location_nic,
+	rnpm_mc_location_mac,
+};
+enum vlan_location_type {
+	rnpm_vlan_location_nic,
+	rnpm_vlan_location_mac,
+};
+struct rnpm_mac_info {
+	struct rnpm_mac_operations ops;
+	// enum rnpm_mac_type             type;
+	u8 addr[ETH_ALEN];
+	u8 perm_addr[ETH_ALEN];
+	/* prefix for World Wide Node Name (WWNN) */
+	u16 wwnn_prefix;
+	/* prefix for World Wide Port Name (WWPN) */
+	u16 wwpn_prefix;
+	u16 max_msix_vectors;
+#define RNPM_MAX_MTA 128
+	u32 mta_shadow[RNPM_MAX_MTA];
+	s32 mc_filter_type;
+	u32 mc_location;
+	u32 mcft_size;
+	u32 vft_size;
+	u32 vlan_location;
+	u32 num_rar_entries;
+	u32 rar_highwater;
+	u32 rx_pb_size;
+	u32 max_tx_queues;
+	u32 max_rx_queues;
+	u32 reg_off;
+	u32 orig_autoc;
+	u32 cached_autoc;
+	u32 orig_autoc2;
+	bool orig_link_settings_stored;
+	bool autotry_restart;
+	u8 mac_flags;
+	bool autoneg;
+	struct rnpm_thermal_sensor_data thermal_sensor_data;
+	bool thermal_sensor_enabled;
+	bool duplex;
+};
+
+struct rnpm_phy_info {
+	struct rnpm_phy_operations ops;
+	struct mdio_if_info mdio;
+	enum rnpm_phy_type type;
+	u32 id;
+	u32 phy_addr;
+	bool is_mdix;
+	bool an;
+	u8 mdix;
+	/* Phy register */
+	u32 vb_r[RNPM_PHY_MAX_BASIC_REGISTER_NUM];
+	enum rnpm_sfp_type sfp_type;
+	bool sfp_setup_needed;
+	u32 revision;
+	enum rnpm_media_type media_type;
+	bool reset_disable;
+	rnpm_autoneg_advertised autoneg_advertised;
+	bool smart_speed_active;
+	bool multispeed_fiber;
+	bool reset_if_overtemp;
+};
+
+#include "rnpm_mbx.h"
+
+struct rnpm_pcs_operations {
+	u32 (*read)(struct rnpm_hw *hw, int num, u32 addr);
+	void (*write)(struct rnpm_hw *hw, int num, u32 addr, u32 value);
+};
+
+struct rnpm_mbx_operations {
+	s32 (*init_params)(struct rnpm_hw *hw);
+	s32 (*read)(struct rnpm_hw *hw, u32 *msg, u16 size, enum MBX_ID mbx_id);
+	s32 (*write)(struct rnpm_hw *hw, u32 *msg, u16 size,
+		     enum MBX_ID mbx_id);
+	s32 (*read_posted)(struct rnpm_hw *hw, u32 *msg, u16 size,
+			   enum MBX_ID mbx_id);
+	s32 (*write_posted)(struct rnpm_hw *hw, u32 *msg, u16 size,
+			    enum MBX_ID mbx_id);
+	s32 (*check_for_msg)(struct rnpm_hw *hw, enum MBX_ID mbx_id);
+	s32 (*check_for_ack)(struct rnpm_hw *hw, enum MBX_ID mbx_id);
+	//	s32 (*check_for_rst)(struct rnpm_hw *, enum MBX_ID);
+	s32 (*configure)(struct rnpm_hw *hw, int nr_vec, bool enable);
+};
+
+struct rnpm_mbx_stats {
+	u32 msgs_tx;
+	u32 msgs_rx;
+
+	u32 acks;
+	u32 reqs;
+	u32 rsts;
+};
+
+struct rnpm_pcs_info {
+	struct rnpm_pcs_operations ops;
+	int pcs_count;
+};
+
+struct rnpm_err_pkts_init_info {
+	u64 wdt[4];
+	u64 csum[4];
+	u64 code[4];
+	u64 crc[4];
+	u64 slen[4];
+	u64 glen[4];
+	u64 iph[4];
+	u64 len[4];
+	u64 cut[4];
+	u64 drop[4];
+	u64 scsum[4]; /* Software record csum count*/
+};
+
+struct rnpm_mbx_info {
+	struct rnpm_mbx_operations ops;
+	struct rnpm_mbx_stats stats;
+	u32 timeout;
+	u32 usec_delay;
+	u32 v2p_mailbox;
+	u16 size;
+
+	u16 vf_req[64];
+	u16 vf_ack[64];
+	u16 cpu_req;
+	u16 cpu_ack;
+
+	void *reply_dma;
+	dma_addr_t reply_dma_phy;
+	int reply_dma_size;
+
+	struct mutex *lock;
+
+	bool irq_enabled;
+};
+
+#define RNPM_MBX_VF_CPU_SHM_PF_BASE (0xA8000)
+#define RNPM_NCSI_MC_COUNT (11)
+#define RNPM_NCSI_VLAN_COUNT (1)
+
+// 0x500a8fc0,0x501adfc0: #63 cpu<->vf shm
+#define RNPM_VF_CPU_SHM_BASE_NR62 (RNPM_MBX_VF_CPU_SHM_PF_BASE + 62 * 64)
+struct ncsi_shm_info {
+	u32 valid;
+#define RNPM_NCSI_SHM_VALID 0xa5000000
+#define RNPM_NCSI_SHM_VALID_MASK 0xff000000
+#define RNPM_MC_VALID BIT(0)
+#define RNPM_UC_VALID BIT(1)
+#define RNPM_VLAN_VALID BIT(2)
+
+	struct {
+		u32 uc_addr_lo;
+		u32 uc_addr_hi;
+	} uc;
+
+	struct {
+		u32 mc_addr_lo;
+		u32 mc_addr_hi;
+	} mc[RNPM_NCSI_MC_COUNT];
+	u32 ncsi_vlan;
+};
+
+struct rnpm_hw {
+	void *back;
+	u8 __iomem *hw_addr;
+	u8 __iomem *ring_msix_base;
+	u8 __iomem *rpu_addr;
+	spinlock_t *pf_setup_lock;
+
+	u8 pfvfnum; // fun
+	u8 num;
+	u8 nr_lane;
+	int speed;
+	int ablity_speed;
+	u8 link; // up/down
+
+	u8 ncsi_en;
+	u8 ncsi_rar_entries;
+	u16 ncsi_mc_count;
+	u16 ncsi_vlan_count;
+	u32 ncsi_vf_cpu_shm_pf_base;
+	u8 rpu_en;
+	u8 rpu_availble;
+	u8 max_speed_1g;
+
+	u8 pci_gen;
+	u8 pci_lanes;
+	struct pci_dev *pdev;
+
+	u32 ccode;
+	u16 device_id;
+	u16 vendor_id;
+	u16 subsystem_device_id;
+	u16 subsystem_vendor_id;
+	char lane_mask;
+	// u16 mac_type;
+	u16 phy_type;
+
+	u32 fw_version;
+	u32 axi_mhz;
+	u32 fw_uid;
+	union {
+		u8 port_id[4];
+		u32 port_ids;
+	};
+	u8 is_backplane : 1;
+	u8 is_sgmii : 1;
+	u8 force_speed_stat : 2;
+#define FORCE_SPEED_STAT_DISABLED 0
+#define FORCE_SPEED_STAT_1G 1
+#define FORCE_SPEED_STAT_10G 2
+	u8 duplex : 1;
+	u8 single_lane_link_evt_ctrl_ablity : 1;
+	u8 fw_lldp_ablity : 1;
+	u32 supported_link;
+
+	u32 dma_version;
+	u32 wol;
+	int dma_split_size;
+	enum rnpm_rss_type rss_type;
+	struct rnpm_mac_info mac;
+	struct rnpm_addr_filter_info addr_ctrl;
+	struct rnpm_fc_info fc;
+	struct rnpm_phy_info phy;
+	struct rnpm_eeprom_info eeprom;
+	struct rnpm_bus_info bus;
+	struct rnpm_mbx_info mbx;
+	struct rnpm_pcs_info pcs;
+	struct rnpm_err_pkts_init_info err_pkts_init;
+	bool adapter_stopped;
+	bool force_full_reset;
+	bool mng_fw_enabled;
+	bool wol_enabled;
+	unsigned long wol_supported;
+	int mode;
+	int default_rx_queue;
+	int usecstocount;
+
+#define RNPM_NET_FEATURE_SG ((u32)(1 << 0))
+#define RNPM_NET_FEATURE_TX_CHECKSUM ((u32)(1 << 1))
+#define RNPM_NET_FEATURE_RX_CHECKSUM ((u32)(1 << 2))
+#define RNPM_NET_FEATURE_TSO ((u32)(1 << 3))
+#define RNPM_NET_FEATURE_TX_UDP_TUNNEL BIT(4)
+#define RNPM_NET_FEATURE_VLAN_FILTER BIT(5)
+#define RNPM_NET_FEATURE_VLAN_OFFLOAD BIT(6)
+#define RNPM_NET_FEATURE_RX_NTUPLE_FILTER BIT(7)
+#define RNPM_NET_FEATURE_TCAM BIT(8)
+#define RNPM_NET_FEATURE_RX_HASH BIT(9)
+#define RNPM_NET_FEATURE_RX_FCS BIT(10)
+	u32 feature_flags;
+
+	struct {
+		int version;
+		int len;
+		int flag;
+	} dump;
+};
+
+struct rnpm_info {
+	// enum rnpm_mac_type		mac;
+	enum rnpm_rss_type rss_type;
+	s32 (*get_invariants)(struct rnpm_hw *hw);
+	struct rnpm_mac_operations *mac_ops;
+	struct rnpm_phy_operations *phy_ops;
+	struct rnpm_mbx_operations *mbx_ops;
+	struct rnpm_pcs_operations *pcs_ops;
+
+	bool one_pf_with_two_dma;
+	int reg_off;
+	int adapter_cnt;
+	int hi_dma;
+	int total_queue_pair_cnts;
+	int queue_depth;
+	int total_msix_table;
+	int total_layer2_count;
+	int total_tuple5_count;
+	bool mac_padding;
+	int dma2_in_1pf;
+	char *hw_addr;
+	struct {
+		u16 tx_work_limit;
+		u32 rx_usecs;
+		u32 rx_frames;
+		u32 tx_usecs;
+		u32 tx_frames;
+	} coalesce;
+};
+
+/* Error Codes */
+#define RNPM_ERR_EEPROM -1
+#define RNPM_ERR_EEPROM_CHECKSUM -2
+#define RNPM_ERR_PHY -3
+#define RNPM_ERR_CONFIG -4
+#define RNPM_ERR_PARAM -5
+#define RNPM_ERR_MAC_TYPE -6
+#define RNPM_ERR_UNKNOWN_PHY -7
+#define RNPM_ERR_LINK_SETUP -8
+#define RNPM_ERR_ADAPTER_STOPPED -9
+#define RNPM_ERR_INVALID_MAC_ADDR -10
+#define RNPM_ERR_DEVICE_NOT_SUPPORTED -11
+#define RNPM_ERR_MASTER_REQUESTS_PENDING -12
+#define RNPM_ERR_INVALID_LINK_SETTINGS -13
+#define RNPM_ERR_AUTONEG_NOT_COMPLETE -14
+#define RNPM_ERR_RESET_FAILED -15
+#define RNPM_ERR_SWFW_SYNC -16
+#define RNPM_ERR_PHY_ADDR_INVALID -17
+#define RNPM_ERR_I2C -18
+#define RNPM_ERR_SFP_NOT_SUPPORTED -19
+#define RNPM_ERR_SFP_NOT_PRESENT -20
+#define RNPM_ERR_SFP_NO_INIT_SEQ_PRESENT -21
+#define RNPM_ERR_FDIR_REINIT_FAILED -23
+#define RNPM_ERR_EEPROM_VERSION -24
+#define RNPM_ERR_NO_SPACE -25
+#define RNPM_ERR_OVERTEMP -26
+#define RNPM_ERR_FC_NOT_NEGOTIATED -27
+#define RNPM_ERR_FC_NOT_SUPPORTED -28
+#define RNPM_ERR_SFP_SETUP_NOT_COMPLETE -30
+#define RNPM_ERR_PBA_SECTION -31
+#define RNPM_ERR_INVALID_ARGUMENT -32
+#define RNPM_ERR_HOST_INTERFACE_COMMAND -33
+#define RNPM_NOT_IMPLEMENTED 0x7FFFFFFF
+
+#define RNPM_RAH_AV 0x80000000
+/* eth fix code */
+#define RNPM_FCTRL_BPE BIT(10)
+#define RNPM_FCTRL_UPE BIT(9)
+#define RNPM_FCTRL_MPE BIT(8)
+
+#define RNPM_MCSTCTRL_MTA BIT(2)
+#define RNPM_MCSTCTRL_UTA BIT(3)
+
+#define RNPM_MAX_LAYER2_FILTERS (16)
+#define RNPM_MAX_TUPLE5_FILTERS (128)
+#define RNPM_MAX_TCAM_FILTERS (4096)
+
+#define RNPM_SRC_IP_MASK BIT(0)
+#define RNPM_DST_IP_MASK BIT(1)
+#define RNPM_SRC_PORT_MASK BIT(2)
+#define RNPM_DST_PORT_MASK BIT(3)
+#define RNPM_L4_PROTO_MASK BIT(4)
+#endif /* _RNPM_TYPE_H_ */
diff --git a/drivers/net/ethernet/mucse/rnpm/version.h b/drivers/net/ethernet/mucse/rnpm/version.h
new file mode 100644
index 000000000000..dcda8289aeac
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpm/version.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2022 - 2024 Mucse Corporation. */
+
+#ifndef VERSION_H
+#define VERSION_H
+#define GIT_COMMIT " 3cacaa5"
+#endif
-- 
Gitee