[PATCH] e1000: Fix loopback logic
[safe/jmp/linux-2.6] / drivers / net / e1000 / e1000_ethtool.c
index 54ae880..ceb9cd0 100644 (file)
@@ -96,8 +96,18 @@ static const struct e1000_stats e1000_gstrings_stats[] = {
        { "rx_header_split", E1000_STAT(rx_hdr_split) },
        { "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) },
 };
-#define E1000_STATS_LEN        \
+
+#ifdef CONFIG_E1000_MQ
+#define E1000_QUEUE_STATS_LEN \
+       (((struct e1000_adapter *)netdev->priv)->num_tx_queues + \
+        ((struct e1000_adapter *)netdev->priv)->num_rx_queues) \
+       * (sizeof(struct e1000_queue_stats) / sizeof(uint64_t))
+#else
+#define E1000_QUEUE_STATS_LEN 0
+#endif
+#define E1000_GLOBAL_STATS_LEN \
        sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats)
+#define E1000_STATS_LEN (E1000_GLOBAL_STATS_LEN + E1000_QUEUE_STATS_LEN)
 static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register test  (offline)", "Eeprom test    (offline)",
        "Interrupt test (offline)", "Loopback test  (offline)",
@@ -614,8 +624,8 @@ e1000_set_ringparam(struct net_device *netdev,
        struct e1000_rx_ring *rxdr, *rx_old, *rx_new;
        int i, err, tx_ring_size, rx_ring_size;
 
-       tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_queues;
-       rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_queues;
+       tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues;
+       rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues;
 
        if (netif_running(adapter->netdev))
                e1000_down(adapter);
@@ -654,10 +664,10 @@ e1000_set_ringparam(struct net_device *netdev,
                E1000_MAX_TXD : E1000_MAX_82544_TXD));
        E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); 
 
-       for (i = 0; i < adapter->num_queues; i++) {
+       for (i = 0; i < adapter->num_tx_queues; i++)
                txdr[i].count = txdr->count;
+       for (i = 0; i < adapter->num_rx_queues; i++)
                rxdr[i].count = rxdr->count;
-       }
 
        if(netif_running(adapter->netdev)) {
                /* Try to get new resources before deleting old */
@@ -1316,22 +1326,33 @@ e1000_set_phy_loopback(struct e1000_adapter *adapter)
 static int
 e1000_setup_loopback_test(struct e1000_adapter *adapter)
 {
+       struct e1000_hw *hw = &adapter->hw;
        uint32_t rctl;
 
-       if(adapter->hw.media_type == e1000_media_type_fiber ||
-          adapter->hw.media_type == e1000_media_type_internal_serdes) {
-               if(adapter->hw.mac_type == e1000_82545 ||
-                  adapter->hw.mac_type == e1000_82546 ||
-                  adapter->hw.mac_type == e1000_82545_rev_3 ||
-                  adapter->hw.mac_type == e1000_82546_rev_3)
+       if (hw->media_type == e1000_media_type_fiber ||
+           hw->media_type == e1000_media_type_internal_serdes) {
+               switch (hw->mac_type) {
+               case e1000_82545:
+               case e1000_82546:
+               case e1000_82545_rev_3:
+               case e1000_82546_rev_3:
                        return e1000_set_phy_loopback(adapter);
-               else {
-                       rctl = E1000_READ_REG(&adapter->hw, RCTL);
+                       break;
+               case e1000_82571:
+               case e1000_82572:
+#define E1000_SERDES_LB_ON 0x410
+                       e1000_set_phy_loopback(adapter);
+                       E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_ON);
+                       msec_delay(10);
+                       return 0;
+                       break;
+               default:
+                       rctl = E1000_READ_REG(hw, RCTL);
                        rctl |= E1000_RCTL_LBM_TCVR;
-                       E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
+                       E1000_WRITE_REG(hw, RCTL, rctl);
                        return 0;
                }
-       } else if(adapter->hw.media_type == e1000_media_type_copper)
+       } else if (hw->media_type == e1000_media_type_copper)
                return e1000_set_phy_loopback(adapter);
 
        return 7;
@@ -1340,27 +1361,38 @@ e1000_setup_loopback_test(struct e1000_adapter *adapter)
 static void
 e1000_loopback_cleanup(struct e1000_adapter *adapter)
 {
+       struct e1000_hw *hw = &adapter->hw;
        uint32_t rctl;
        uint16_t phy_reg;
 
-       rctl = E1000_READ_REG(&adapter->hw, RCTL);
+       rctl = E1000_READ_REG(hw, RCTL);
        rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
-       E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
+       E1000_WRITE_REG(hw, RCTL, rctl);
 
-       if(adapter->hw.media_type == e1000_media_type_copper ||
-          ((adapter->hw.media_type == e1000_media_type_fiber ||
-            adapter->hw.media_type == e1000_media_type_internal_serdes) &&
-           (adapter->hw.mac_type == e1000_82545 ||
-            adapter->hw.mac_type == e1000_82546 ||
-            adapter->hw.mac_type == e1000_82545_rev_3 ||
-            adapter->hw.mac_type == e1000_82546_rev_3))) {
-               adapter->hw.autoneg = TRUE;
-               e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
-               if(phy_reg & MII_CR_LOOPBACK) {
+       switch (hw->mac_type) {
+       case e1000_82571:
+       case e1000_82572:
+               if (hw->media_type == e1000_media_type_fiber ||
+                   hw->media_type == e1000_media_type_internal_serdes) {
+#define E1000_SERDES_LB_OFF 0x400
+                       E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_OFF);
+                       msec_delay(10);
+                       break;
+               }
+               /* Fall Through */
+       case e1000_82545:
+       case e1000_82546:
+       case e1000_82545_rev_3:
+       case e1000_82546_rev_3:
+       default:
+               hw->autoneg = TRUE;
+               e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
+               if (phy_reg & MII_CR_LOOPBACK) {
                        phy_reg &= ~MII_CR_LOOPBACK;
-                       e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
-                       e1000_phy_reset(&adapter->hw);
+                       e1000_write_phy_reg(hw, PHY_CTRL, phy_reg);
+                       e1000_phy_reset(hw);
                }
+               break;
        }
 }
 
@@ -1746,19 +1778,43 @@ e1000_get_ethtool_stats(struct net_device *netdev,
                struct ethtool_stats *stats, uint64_t *data)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
+#ifdef CONFIG_E1000_MQ
+       uint64_t *queue_stat;
+       int stat_count = sizeof(struct e1000_queue_stats) / sizeof(uint64_t);
+       int j, k;
+#endif
        int i;
 
        e1000_update_stats(adapter);
-       for(i = 0; i < E1000_STATS_LEN; i++) {
-               char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;  
-               data[i] = (e1000_gstrings_stats[i].sizeof_stat == 
+       for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
+               char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;
+               data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
                        sizeof(uint64_t)) ? *(uint64_t *)p : *(uint32_t *)p;
        }
+#ifdef CONFIG_E1000_MQ
+       for (j = 0; j < adapter->num_tx_queues; j++) {
+               queue_stat = (uint64_t *)&adapter->tx_ring[j].tx_stats;
+               for (k = 0; k < stat_count; k++)
+                       data[i + k] = queue_stat[k];
+               i += k;
+       }
+       for (j = 0; j < adapter->num_rx_queues; j++) {
+               queue_stat = (uint64_t *)&adapter->rx_ring[j].rx_stats;
+               for (k = 0; k < stat_count; k++)
+                       data[i + k] = queue_stat[k];
+               i += k;
+       }
+#endif
+/*     BUG_ON(i != E1000_STATS_LEN); */
 }
 
 static void 
 e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
 {
+#ifdef CONFIG_E1000_MQ
+       struct e1000_adapter *adapter = netdev_priv(netdev);
+#endif
+       uint8_t *p = data;
        int i;
 
        switch(stringset) {
@@ -1767,11 +1823,26 @@ e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
                        E1000_TEST_LEN*ETH_GSTRING_LEN);
                break;
        case ETH_SS_STATS:
-               for (i=0; i < E1000_STATS_LEN; i++) {
-                       memcpy(data + i * ETH_GSTRING_LEN, 
-                       e1000_gstrings_stats[i].stat_string,
-                       ETH_GSTRING_LEN);
+               for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
+                       memcpy(p, e1000_gstrings_stats[i].stat_string,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+#ifdef CONFIG_E1000_MQ
+               for (i = 0; i < adapter->num_tx_queues; i++) {
+                       sprintf(p, "tx_queue_%u_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "tx_queue_%u_bytes", i);
+                       p += ETH_GSTRING_LEN;
                }
+               for (i = 0; i < adapter->num_rx_queues; i++) {
+                       sprintf(p, "rx_queue_%u_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "rx_queue_%u_bytes", i);
+                       p += ETH_GSTRING_LEN;
+               }
+#endif
+/*             BUG_ON(p - data != E1000_STATS_LEN * ETH_GSTRING_LEN); */
                break;
        }
 }