+static void bnx2x_wait_for_link(struct bnx2x *bp, u8 link_up)
+{
+ int cnt = 1000;
+
+ if (link_up)
+ while (bnx2x_link_test(bp) && cnt--)
+ msleep(10);
+}
+
+static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
+{
+ unsigned int pkt_size, num_pkts, i;
+ struct sk_buff *skb;
+ unsigned char *packet;
+ struct bnx2x_fastpath *fp = &bp->fp[0];
+ u16 tx_start_idx, tx_idx;
+ u16 rx_start_idx, rx_idx;
+ u16 pkt_prod;
+ struct sw_tx_bd *tx_buf;
+ struct eth_tx_bd *tx_bd;
+ dma_addr_t mapping;
+ union eth_rx_cqe *cqe;
+ u8 cqe_fp_flags;
+ struct sw_rx_bd *rx_buf;
+ u16 len;
+ int rc = -ENODEV;
+
+ if (loopback_mode == BNX2X_MAC_LOOPBACK) {
+ bp->link_params.loopback_mode = LOOPBACK_BMAC;
+ bnx2x_acquire_phy_lock(bp);
+ bnx2x_phy_init(&bp->link_params, &bp->link_vars);
+ bnx2x_release_phy_lock(bp);
+
+ } else if (loopback_mode == BNX2X_PHY_LOOPBACK) {
+ bp->link_params.loopback_mode = LOOPBACK_XGXS_10;
+ bnx2x_acquire_phy_lock(bp);
+ bnx2x_phy_init(&bp->link_params, &bp->link_vars);
+ bnx2x_release_phy_lock(bp);
+ /* wait until link state is restored */
+ bnx2x_wait_for_link(bp, link_up);
+
+ } else
+ return -EINVAL;
+
+ pkt_size = 1514;
+ skb = netdev_alloc_skb(bp->dev, bp->rx_buf_size);
+ if (!skb) {
+ rc = -ENOMEM;
+ goto test_loopback_exit;
+ }
+ packet = skb_put(skb, pkt_size);
+ memcpy(packet, bp->dev->dev_addr, ETH_ALEN);
+ memset(packet + ETH_ALEN, 0, (ETH_HLEN - ETH_ALEN));
+ for (i = ETH_HLEN; i < pkt_size; i++)
+ packet[i] = (unsigned char) (i & 0xff);
+
+ num_pkts = 0;
+ tx_start_idx = le16_to_cpu(*fp->tx_cons_sb);
+ rx_start_idx = le16_to_cpu(*fp->rx_cons_sb);
+
+ pkt_prod = fp->tx_pkt_prod++;
+ tx_buf = &fp->tx_buf_ring[TX_BD(pkt_prod)];
+ tx_buf->first_bd = fp->tx_bd_prod;
+ tx_buf->skb = skb;
+
+ tx_bd = &fp->tx_desc_ring[TX_BD(fp->tx_bd_prod)];
+ mapping = pci_map_single(bp->pdev, skb->data,
+ skb_headlen(skb), PCI_DMA_TODEVICE);
+ tx_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
+ tx_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
+ tx_bd->nbd = cpu_to_le16(1);
+ tx_bd->nbytes = cpu_to_le16(skb_headlen(skb));
+ tx_bd->vlan = cpu_to_le16(pkt_prod);
+ tx_bd->bd_flags.as_bitfield = (ETH_TX_BD_FLAGS_START_BD |
+ ETH_TX_BD_FLAGS_END_BD);
+ tx_bd->general_data = ((UNICAST_ADDRESS <<
+ ETH_TX_BD_ETH_ADDR_TYPE_SHIFT) | 1);
+
+ fp->hw_tx_prods->bds_prod =
+ cpu_to_le16(le16_to_cpu(fp->hw_tx_prods->bds_prod) + 1);
+ mb(); /* FW restriction: must not reorder writing nbd and packets */
+ fp->hw_tx_prods->packets_prod =
+ cpu_to_le32(le32_to_cpu(fp->hw_tx_prods->packets_prod) + 1);
+ DOORBELL(bp, FP_IDX(fp), 0);
+
+ mmiowb();
+
+ num_pkts++;
+ fp->tx_bd_prod++;
+ bp->dev->trans_start = jiffies;
+
+ udelay(100);
+
+ tx_idx = le16_to_cpu(*fp->tx_cons_sb);
+ if (tx_idx != tx_start_idx + num_pkts)
+ goto test_loopback_exit;
+
+ rx_idx = le16_to_cpu(*fp->rx_cons_sb);
+ if (rx_idx != rx_start_idx + num_pkts)
+ goto test_loopback_exit;
+
+ cqe = &fp->rx_comp_ring[RCQ_BD(fp->rx_comp_cons)];
+ cqe_fp_flags = cqe->fast_path_cqe.type_error_flags;
+ if (CQE_TYPE(cqe_fp_flags) || (cqe_fp_flags & ETH_RX_ERROR_FALGS))
+ goto test_loopback_rx_exit;
+
+ len = le16_to_cpu(cqe->fast_path_cqe.pkt_len);
+ if (len != pkt_size)
+ goto test_loopback_rx_exit;
+
+ rx_buf = &fp->rx_buf_ring[RX_BD(fp->rx_bd_cons)];
+ skb = rx_buf->skb;
+ skb_reserve(skb, cqe->fast_path_cqe.placement_offset);
+ for (i = ETH_HLEN; i < pkt_size; i++)
+ if (*(skb->data + i) != (unsigned char) (i & 0xff))
+ goto test_loopback_rx_exit;
+
+ rc = 0;
+
+test_loopback_rx_exit:
+ bp->dev->last_rx = jiffies;
+
+ fp->rx_bd_cons = NEXT_RX_IDX(fp->rx_bd_cons);
+ fp->rx_bd_prod = NEXT_RX_IDX(fp->rx_bd_prod);
+ fp->rx_comp_cons = NEXT_RCQ_IDX(fp->rx_comp_cons);
+ fp->rx_comp_prod = NEXT_RCQ_IDX(fp->rx_comp_prod);
+
+ /* Update producers */
+ bnx2x_update_rx_prod(bp, fp, fp->rx_bd_prod, fp->rx_comp_prod,
+ fp->rx_sge_prod);
+ mmiowb(); /* keep prod updates ordered */
+
+test_loopback_exit:
+ bp->link_params.loopback_mode = LOOPBACK_NONE;
+
+ return rc;
+}
+
+static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up)
+{
+ int rc = 0;
+
+ if (!netif_running(bp->dev))
+ return BNX2X_LOOPBACK_FAILED;
+
+ bnx2x_netif_stop(bp);
+
+ if (bnx2x_run_loopback(bp, BNX2X_MAC_LOOPBACK, link_up)) {
+ DP(NETIF_MSG_PROBE, "MAC loopback failed\n");
+ rc |= BNX2X_MAC_LOOPBACK_FAILED;
+ }
+
+ if (bnx2x_run_loopback(bp, BNX2X_PHY_LOOPBACK, link_up)) {
+ DP(NETIF_MSG_PROBE, "PHY loopback failed\n");
+ rc |= BNX2X_PHY_LOOPBACK_FAILED;
+ }
+
+ bnx2x_netif_start(bp);
+
+ return rc;
+}
+
+#define CRC32_RESIDUAL 0xdebb20e3
+
+static int bnx2x_test_nvram(struct bnx2x *bp)
+{
+ static const struct {
+ int offset;
+ int size;
+ } nvram_tbl[] = {
+ { 0, 0x14 }, /* bootstrap */
+ { 0x14, 0xec }, /* dir */
+ { 0x100, 0x350 }, /* manuf_info */
+ { 0x450, 0xf0 }, /* feature_info */
+ { 0x640, 0x64 }, /* upgrade_key_info */
+ { 0x6a4, 0x64 },
+ { 0x708, 0x70 }, /* manuf_key_info */
+ { 0x778, 0x70 },
+ { 0, 0 }
+ };
+ u32 buf[0x350 / 4];
+ u8 *data = (u8 *)buf;
+ int i, rc;
+ u32 magic, csum;
+
+ rc = bnx2x_nvram_read(bp, 0, data, 4);
+ if (rc) {
+ DP(NETIF_MSG_PROBE, "magic value read (rc -%d)\n", -rc);
+ goto test_nvram_exit;
+ }
+
+ magic = be32_to_cpu(buf[0]);
+ if (magic != 0x669955aa) {
+ DP(NETIF_MSG_PROBE, "magic value (0x%08x)\n", magic);
+ rc = -ENODEV;
+ goto test_nvram_exit;
+ }
+
+ for (i = 0; nvram_tbl[i].size; i++) {
+
+ rc = bnx2x_nvram_read(bp, nvram_tbl[i].offset, data,
+ nvram_tbl[i].size);
+ if (rc) {
+ DP(NETIF_MSG_PROBE,
+ "nvram_tbl[%d] read data (rc -%d)\n", i, -rc);
+ goto test_nvram_exit;
+ }
+
+ csum = ether_crc_le(nvram_tbl[i].size, data);
+ if (csum != CRC32_RESIDUAL) {
+ DP(NETIF_MSG_PROBE,
+ "nvram_tbl[%d] csum value (0x%08x)\n", i, csum);
+ rc = -ENODEV;
+ goto test_nvram_exit;
+ }
+ }
+
+test_nvram_exit:
+ return rc;
+}
+
+static int bnx2x_test_intr(struct bnx2x *bp)
+{
+ struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config);
+ int i, rc;
+
+ if (!netif_running(bp->dev))
+ return -ENODEV;
+
+ config->hdr.length_6b = 0;
+ config->hdr.offset = 0;
+ config->hdr.client_id = BP_CL_ID(bp);
+ config->hdr.reserved1 = 0;
+
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
+ U64_HI(bnx2x_sp_mapping(bp, mac_config)),
+ U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0);
+ if (rc == 0) {
+ bp->set_mac_pending++;
+ for (i = 0; i < 10; i++) {
+ if (!bp->set_mac_pending)
+ break;
+ msleep_interruptible(10);
+ }
+ if (i == 10)
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
+static void bnx2x_self_test(struct net_device *dev,
+ struct ethtool_test *etest, u64 *buf)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+
+ memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS);
+
+ if (!netif_running(dev))
+ return;
+
+ /* offline tests are not suppoerted in MF mode */
+ if (IS_E1HMF(bp))
+ etest->flags &= ~ETH_TEST_FL_OFFLINE;
+
+ if (etest->flags & ETH_TEST_FL_OFFLINE) {
+ u8 link_up;
+
+ link_up = bp->link_vars.link_up;
+ bnx2x_nic_unload(bp, UNLOAD_NORMAL);
+ bnx2x_nic_load(bp, LOAD_DIAG);
+ /* wait until link state is restored */
+ bnx2x_wait_for_link(bp, link_up);
+
+ if (bnx2x_test_registers(bp) != 0) {
+ buf[0] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
+ if (bnx2x_test_memory(bp) != 0) {
+ buf[1] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
+ buf[2] = bnx2x_test_loopback(bp, link_up);
+ if (buf[2] != 0)
+ etest->flags |= ETH_TEST_FL_FAILED;
+
+ bnx2x_nic_unload(bp, UNLOAD_NORMAL);
+ bnx2x_nic_load(bp, LOAD_NORMAL);
+ /* wait until link state is restored */
+ bnx2x_wait_for_link(bp, link_up);
+ }
+ if (bnx2x_test_nvram(bp) != 0) {
+ buf[3] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
+ if (bnx2x_test_intr(bp) != 0) {
+ buf[4] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
+ if (bp->port.pmf)
+ if (bnx2x_link_test(bp) != 0) {
+ buf[5] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
+ buf[7] = bnx2x_mc_assert(bp);
+ if (buf[7] != 0)
+ etest->flags |= ETH_TEST_FL_FAILED;
+
+#ifdef BNX2X_EXTRA_DEBUG
+ bnx2x_panic_dump(bp);
+#endif
+}
+
+static const struct {
+ long offset;
+ int size;
+ u32 flags;
+#define STATS_FLAGS_PORT 1
+#define STATS_FLAGS_FUNC 2
+ u8 string[ETH_GSTRING_LEN];