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