]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/qlcnic/qlcnic_ethtool.c
Merge tag 'v2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / qlcnic / qlcnic_ethtool.c
index 9328d59e21e0c6c2ff8011b19eb30f5f041fed5d..ec21d24015c485a7153e4262026f89d1c73af6c3 100644 (file)
@@ -78,7 +78,25 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
 
 };
 
+static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
+       "rx unicast frames",
+       "rx multicast frames",
+       "rx broadcast frames",
+       "rx dropped frames",
+       "rx errors",
+       "rx local frames",
+       "rx numbytes",
+       "tx unicast frames",
+       "tx multicast frames",
+       "tx broadcast frames",
+       "tx dropped frames",
+       "tx errors",
+       "tx local frames",
+       "tx numbytes",
+};
+
 #define QLCNIC_STATS_LEN       ARRAY_SIZE(qlcnic_gstrings_stats)
+#define QLCNIC_DEVICE_STATS_LEN        ARRAY_SIZE(qlcnic_device_gstrings_stats)
 
 static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register_Test_on_offline",
@@ -96,10 +114,10 @@ static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
 static const u32 diag_registers[] = {
        CRB_CMDPEG_STATE,
        CRB_RCVPEG_STATE,
-       CRB_XG_STATE_P3,
+       CRB_XG_STATE_P3P,
        CRB_FW_CAPABILITIES_1,
        ISR_INT_STATE_REG,
-       QLCNIC_CRB_DEV_REF_COUNT,
+       QLCNIC_CRB_DRV_ACTIVE,
        QLCNIC_CRB_DEV_STATE,
        QLCNIC_CRB_DRV_STATE,
        QLCNIC_CRB_DRV_SCRATCH,
@@ -115,9 +133,13 @@ static const u32 diag_registers[] = {
        -1
 };
 
+#define QLCNIC_MGMT_API_VERSION        2
+#define QLCNIC_DEV_INFO_SIZE   1
+#define QLCNIC_ETHTOOL_REGS_VER        2
 static int qlcnic_get_regs_len(struct net_device *dev)
 {
-       return sizeof(diag_registers) + QLCNIC_RING_REGS_LEN;
+       return sizeof(diag_registers) + QLCNIC_RING_REGS_LEN +
+                               QLCNIC_DEV_INFO_SIZE + 1;
 }
 
 static int qlcnic_get_eeprom_len(struct net_device *dev)
@@ -185,9 +207,9 @@ qlcnic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                        goto skip;
                }
 
-               val = QLCRD32(adapter, P3_LINK_SPEED_REG(pcifn));
-               ecmd->speed = P3_LINK_SPEED_MHZ *
-                       P3_LINK_SPEED_VAL(pcifn, val);
+               val = QLCRD32(adapter, P3P_LINK_SPEED_REG(pcifn));
+               ecmd->speed = P3P_LINK_SPEED_MHZ *
+                       P3P_LINK_SPEED_VAL(pcifn, val);
                ecmd->duplex = DUPLEX_FULL;
                ecmd->autoneg = AUTONEG_DISABLE;
        } else
@@ -198,42 +220,42 @@ skip:
        ecmd->transceiver = XCVR_EXTERNAL;
 
        switch (adapter->ahw.board_type) {
-       case QLCNIC_BRDTYPE_P3_REF_QG:
-       case QLCNIC_BRDTYPE_P3_4_GB:
-       case QLCNIC_BRDTYPE_P3_4_GB_MM:
+       case QLCNIC_BRDTYPE_P3P_REF_QG:
+       case QLCNIC_BRDTYPE_P3P_4_GB:
+       case QLCNIC_BRDTYPE_P3P_4_GB_MM:
 
                ecmd->supported |= SUPPORTED_Autoneg;
                ecmd->advertising |= ADVERTISED_Autoneg;
-       case QLCNIC_BRDTYPE_P3_10G_CX4:
-       case QLCNIC_BRDTYPE_P3_10G_CX4_LP:
-       case QLCNIC_BRDTYPE_P3_10000_BASE_T:
+       case QLCNIC_BRDTYPE_P3P_10G_CX4:
+       case QLCNIC_BRDTYPE_P3P_10G_CX4_LP:
+       case QLCNIC_BRDTYPE_P3P_10000_BASE_T:
                ecmd->supported |= SUPPORTED_TP;
                ecmd->advertising |= ADVERTISED_TP;
                ecmd->port = PORT_TP;
                ecmd->autoneg =  adapter->link_autoneg;
                break;
-       case QLCNIC_BRDTYPE_P3_IMEZ:
-       case QLCNIC_BRDTYPE_P3_XG_LOM:
-       case QLCNIC_BRDTYPE_P3_HMEZ:
+       case QLCNIC_BRDTYPE_P3P_IMEZ:
+       case QLCNIC_BRDTYPE_P3P_XG_LOM:
+       case QLCNIC_BRDTYPE_P3P_HMEZ:
                ecmd->supported |= SUPPORTED_MII;
                ecmd->advertising |= ADVERTISED_MII;
                ecmd->port = PORT_MII;
                ecmd->autoneg = AUTONEG_DISABLE;
                break;
-       case QLCNIC_BRDTYPE_P3_10G_SFP_PLUS:
-       case QLCNIC_BRDTYPE_P3_10G_SFP_CT:
-       case QLCNIC_BRDTYPE_P3_10G_SFP_QT:
+       case QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS:
+       case QLCNIC_BRDTYPE_P3P_10G_SFP_CT:
+       case QLCNIC_BRDTYPE_P3P_10G_SFP_QT:
                ecmd->advertising |= ADVERTISED_TP;
                ecmd->supported |= SUPPORTED_TP;
                check_sfp_module = netif_running(dev) &&
                        adapter->has_link_events;
-       case QLCNIC_BRDTYPE_P3_10G_XFP:
+       case QLCNIC_BRDTYPE_P3P_10G_XFP:
                ecmd->supported |= SUPPORTED_FIBRE;
                ecmd->advertising |= ADVERTISED_FIBRE;
                ecmd->port = PORT_FIBRE;
                ecmd->autoneg = AUTONEG_DISABLE;
                break;
-       case QLCNIC_BRDTYPE_P3_10G_TP:
+       case QLCNIC_BRDTYPE_P3P_10G_TP:
                if (adapter->ahw.port_type == QLCNIC_XGBE) {
                        ecmd->autoneg = AUTONEG_DISABLE;
                        ecmd->supported |= (SUPPORTED_FIBRE | SUPPORTED_TP);
@@ -339,14 +361,17 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
        struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;
        struct qlcnic_host_sds_ring *sds_ring;
        u32 *regs_buff = p;
-       int ring, i = 0;
+       int ring, i = 0, j = 0;
 
        memset(p, 0, qlcnic_get_regs_len(dev));
-       regs->version = (1 << 24) | (adapter->ahw.revision_id << 16) |
-           (adapter->pdev)->device;
+       regs->version = (QLCNIC_ETHTOOL_REGS_VER << 24) |
+               (adapter->ahw.revision_id << 16) | (adapter->pdev)->device;
+
+       regs_buff[0] = (0xcafe0000 | (QLCNIC_DEV_INFO_SIZE & 0xffff));
+       regs_buff[1] = QLCNIC_MGMT_API_VERSION;
 
-       for (i = 0; diag_registers[i] != -1; i++)
-               regs_buff[i] = QLCRD32(adapter, diag_registers[i]);
+       for (i = QLCNIC_DEV_INFO_SIZE + 1; diag_registers[j] != -1; j++, i++)
+               regs_buff[i] = QLCRD32(adapter, diag_registers[j]);
 
        if (!test_bit(__QLCNIC_DEV_UP, &adapter->state))
                return;
@@ -374,9 +399,9 @@ static u32 qlcnic_test_link(struct net_device *dev)
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        u32 val;
 
-       val = QLCRD32(adapter, CRB_XG_STATE_P3);
-       val = XG_LINK_STATE_P3(adapter->ahw.pci_func, val);
-       return (val == XG_LINK_UP_P3) ? 0 : 1;
+       val = QLCRD32(adapter, CRB_XG_STATE_P3P);
+       val = XG_LINK_STATE_P3P(adapter->ahw.pci_func, val);
+       return (val == XG_LINK_UP_P3P) ? 0 : 1;
 }
 
 static int
@@ -412,14 +437,8 @@ qlcnic_get_ringparam(struct net_device *dev,
        ring->rx_jumbo_pending = adapter->num_jumbo_rxd;
        ring->tx_pending = adapter->num_txd;
 
-       if (adapter->ahw.port_type == QLCNIC_GBE) {
-               ring->rx_max_pending = MAX_RCV_DESCRIPTORS_1G;
-               ring->rx_jumbo_max_pending = MAX_JUMBO_RCV_DESCRIPTORS_1G;
-       } else {
-               ring->rx_max_pending = MAX_RCV_DESCRIPTORS_10G;
-               ring->rx_jumbo_max_pending = MAX_JUMBO_RCV_DESCRIPTORS_10G;
-       }
-
+       ring->rx_max_pending = adapter->max_rxd;
+       ring->rx_jumbo_max_pending = adapter->max_jumbo_rxd;
        ring->tx_max_pending = MAX_CMD_DESCRIPTORS;
 
        ring->rx_mini_max_pending = 0;
@@ -447,24 +466,17 @@ qlcnic_set_ringparam(struct net_device *dev,
                struct ethtool_ringparam *ring)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
-       u16 max_rcv_desc = MAX_RCV_DESCRIPTORS_10G;
-       u16 max_jumbo_desc = MAX_JUMBO_RCV_DESCRIPTORS_10G;
        u16 num_rxd, num_jumbo_rxd, num_txd;
 
-
        if (ring->rx_mini_pending)
                return -EOPNOTSUPP;
 
-       if (adapter->ahw.port_type == QLCNIC_GBE) {
-               max_rcv_desc = MAX_RCV_DESCRIPTORS_1G;
-               max_jumbo_desc = MAX_JUMBO_RCV_DESCRIPTORS_10G;
-       }
-
        num_rxd = qlcnic_validate_ringparam(ring->rx_pending,
-                       MIN_RCV_DESCRIPTORS, max_rcv_desc, "rx");
+                       MIN_RCV_DESCRIPTORS, adapter->max_rxd, "rx");
 
        num_jumbo_rxd = qlcnic_validate_ringparam(ring->rx_jumbo_pending,
-                       MIN_JUMBO_DESCRIPTORS, max_jumbo_desc, "rx jumbo");
+                       MIN_JUMBO_DESCRIPTORS, adapter->max_jumbo_rxd,
+                                               "rx jumbo");
 
        num_txd = qlcnic_validate_ringparam(ring->tx_pending,
                        MIN_CMD_DESCRIPTORS, MAX_CMD_DESCRIPTORS, "tx");
@@ -618,10 +630,13 @@ static int qlcnic_reg_test(struct net_device *dev)
 
 static int qlcnic_get_sset_count(struct net_device *dev, int sset)
 {
+       struct qlcnic_adapter *adapter = netdev_priv(dev);
        switch (sset) {
        case ETH_SS_TEST:
                return QLCNIC_TEST_LEN;
        case ETH_SS_STATS:
+               if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+                       return QLCNIC_STATS_LEN + QLCNIC_DEVICE_STATS_LEN;
                return QLCNIC_STATS_LEN;
        default:
                return -EOPNOTSUPP;
@@ -629,6 +644,8 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset)
 }
 
 #define QLC_ILB_PKT_SIZE 64
+#define QLC_NUM_ILB_PKT        16
+#define QLC_ILB_MAX_RCV_LOOP 10
 
 static void qlcnic_create_loopback_buff(unsigned char *data)
 {
@@ -650,24 +667,34 @@ static int qlcnic_do_ilb_test(struct qlcnic_adapter *adapter)
        struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;
        struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0];
        struct sk_buff *skb;
-       int i;
+       int i, loop, cnt = 0;
 
-       for (i = 0; i < 16; i++) {
+       for (i = 0; i < QLC_NUM_ILB_PKT; i++) {
                skb = dev_alloc_skb(QLC_ILB_PKT_SIZE);
                qlcnic_create_loopback_buff(skb->data);
                skb_put(skb, QLC_ILB_PKT_SIZE);
 
                adapter->diag_cnt = 0;
-
                qlcnic_xmit_frame(skb, adapter->netdev);
 
-               msleep(5);
-
-               qlcnic_process_rcv_ring_diag(sds_ring);
+               loop = 0;
+               do {
+                       msleep(1);
+                       qlcnic_process_rcv_ring_diag(sds_ring);
+               } while (loop++ < QLC_ILB_MAX_RCV_LOOP &&
+                        !adapter->diag_cnt);
 
                dev_kfree_skb_any(skb);
+
                if (!adapter->diag_cnt)
-                       return -1;
+                       dev_warn(&adapter->pdev->dev, "ILB Test: %dth packet"
+                               " not recevied\n", i + 1);
+               else
+                       cnt++;
+       }
+       if (cnt != i) {
+               dev_warn(&adapter->pdev->dev, "ILB Test failed\n");
+               return -1;
        }
        return 0;
 }
@@ -687,6 +714,11 @@ static int qlcnic_loopback_test(struct net_device *netdev)
        if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
                return -EIO;
 
+       if (qlcnic_request_quiscent_mode(adapter)) {
+               clear_bit(__QLCNIC_RESETTING, &adapter->state);
+               return -EIO;
+       }
+
        ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST);
        if (ret)
                goto clear_it;
@@ -703,6 +735,7 @@ done:
        qlcnic_diag_free_res(netdev, max_sds_rings);
 
 clear_it:
+       qlcnic_clear_quiscent_mode(adapter);
        adapter->max_sds_rings = max_sds_rings;
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
        return ret;
@@ -747,6 +780,14 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
 {
        memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN);
 
+       data[0] = qlcnic_reg_test(dev);
+       if (data[0])
+               eth_test->flags |= ETH_TEST_FL_FAILED;
+
+       data[1] = (u64) qlcnic_test_link(dev);
+       if (data[1])
+               eth_test->flags |= ETH_TEST_FL_FAILED;
+
        if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
                data[2] = qlcnic_irq_test(dev);
                if (data[2])
@@ -757,21 +798,13 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
                        eth_test->flags |= ETH_TEST_FL_FAILED;
 
        }
-
-       data[0] = qlcnic_reg_test(dev);
-       if (data[0])
-               eth_test->flags |= ETH_TEST_FL_FAILED;
-
-       /* link test */
-       data[1] = (u64) qlcnic_test_link(dev);
-       if (data[1])
-               eth_test->flags |= ETH_TEST_FL_FAILED;
 }
 
 static void
 qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 * data)
 {
-       int index;
+       struct qlcnic_adapter *adapter = netdev_priv(dev);
+       int index, i;
 
        switch (stringset) {
        case ETH_SS_TEST:
@@ -784,16 +817,43 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 * data)
                               qlcnic_gstrings_stats[index].stat_string,
                               ETH_GSTRING_LEN);
                }
-               break;
+               if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
+                       return;
+               for (i = 0; i < QLCNIC_DEVICE_STATS_LEN; index++, i++) {
+                       memcpy(data + index * ETH_GSTRING_LEN,
+                              qlcnic_device_gstrings_stats[i],
+                              ETH_GSTRING_LEN);
+               }
        }
 }
 
+#define QLCNIC_FILL_ESWITCH_STATS(VAL1) \
+       (((VAL1) == QLCNIC_ESW_STATS_NOT_AVAIL) ? 0 : VAL1)
+
+static void
+qlcnic_fill_device_stats(int *index, u64 *data,
+               struct __qlcnic_esw_statistics *stats)
+{
+       int ind = *index;
+
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->unicast_frames);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->multicast_frames);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->broadcast_frames);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->dropped_frames);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->errors);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->local_frames);
+       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->numbytes);
+
+       *index = ind;
+}
+
 static void
 qlcnic_get_ethtool_stats(struct net_device *dev,
                             struct ethtool_stats *stats, u64 * data)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
-       int index;
+       struct qlcnic_esw_statistics port_stats;
+       int index, ret;
 
        for (index = 0; index < QLCNIC_STATS_LEN; index++) {
                char *p =
@@ -803,8 +863,40 @@ qlcnic_get_ethtool_stats(struct net_device *dev,
                    (qlcnic_gstrings_stats[index].sizeof_stat ==
                     sizeof(u64)) ? *(u64 *)p:(*(u32 *)p);
        }
+
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
+               return;
+
+       memset(&port_stats, 0, sizeof(struct qlcnic_esw_statistics));
+       ret = qlcnic_get_port_stats(adapter, adapter->ahw.pci_func,
+                       QLCNIC_QUERY_RX_COUNTER, &port_stats.rx);
+       if (ret)
+               return;
+
+       qlcnic_fill_device_stats(&index, data, &port_stats.rx);
+
+       ret = qlcnic_get_port_stats(adapter, adapter->ahw.pci_func,
+                       QLCNIC_QUERY_TX_COUNTER, &port_stats.tx);
+       if (ret)
+               return;
+
+       qlcnic_fill_device_stats(&index, data, &port_stats.tx);
 }
 
+static int qlcnic_set_tx_csum(struct net_device *dev, u32 data)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(dev);
+
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED))
+               return -EOPNOTSUPP;
+       if (data)
+               dev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+       else
+               dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+
+       return 0;
+
+}
 static u32 qlcnic_get_tx_csum(struct net_device *dev)
 {
        return dev->features & NETIF_F_IP_CSUM;
@@ -819,7 +911,23 @@ static u32 qlcnic_get_rx_csum(struct net_device *dev)
 static int qlcnic_set_rx_csum(struct net_device *dev, u32 data)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
+
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED))
+               return -EOPNOTSUPP;
+       if (!!data) {
+               adapter->rx_csum = !!data;
+               return 0;
+       }
+
+       if (dev->features & NETIF_F_LRO) {
+               if (qlcnic_config_hw_lro(adapter, QLCNIC_LRO_DISABLED))
+                       return -EIO;
+
+               dev->features &= ~NETIF_F_LRO;
+               qlcnic_send_lro_cleanup(adapter);
+       }
        adapter->rx_csum = !!data;
+       dev_info(&adapter->pdev->dev, "disabling LRO as rx_csum is off\n");
        return 0;
 }
 
@@ -1002,6 +1110,15 @@ static int qlcnic_set_flags(struct net_device *netdev, u32 data)
        if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO))
                return -EINVAL;
 
+       if (!adapter->rx_csum) {
+               dev_info(&adapter->pdev->dev, "rx csum is off, "
+                       "cannot toggle lro\n");
+               return -EINVAL;
+       }
+
+       if ((data & ETH_FLAG_LRO) && (netdev->features & NETIF_F_LRO))
+               return 0;
+
        if (data & ETH_FLAG_LRO) {
                hw_lro = QLCNIC_LRO_ENABLED;
                netdev->features |= NETIF_F_LRO;
@@ -1048,7 +1165,7 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
        .get_pauseparam = qlcnic_get_pauseparam,
        .set_pauseparam = qlcnic_set_pauseparam,
        .get_tx_csum = qlcnic_get_tx_csum,
-       .set_tx_csum = ethtool_op_set_tx_csum,
+       .set_tx_csum = qlcnic_set_tx_csum,
        .set_sg = ethtool_op_set_sg,
        .get_tso = qlcnic_get_tso,
        .set_tso = qlcnic_set_tso,