]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/realtek/r8169.c
Merge tag 'mfd-fixes-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
[karo-tx-linux.git] / drivers / net / ethernet / realtek / r8169.c
index f790f61ea78a2b4f1008da82eca29132ff5bdcc0..b4f21232019a98c7e0afd9e5a43a5a160da765fe 100644 (file)
@@ -637,6 +637,9 @@ enum rtl_register_content {
        /* _TBICSRBit */
        TBILinkOK       = 0x02000000,
 
+       /* ResetCounterCommand */
+       CounterReset    = 0x1,
+
        /* DumpCounterCommand */
        CounterDump     = 0x8,
 
@@ -747,6 +750,13 @@ struct rtl8169_counters {
        __le16  tx_underun;
 };
 
+struct rtl8169_tc_offsets {
+       bool    inited;
+       __le64  tx_errors;
+       __le32  tx_multi_collision;
+       __le16  tx_aborted;
+};
+
 enum rtl_flag {
        RTL_FLAG_TASK_ENABLED,
        RTL_FLAG_TASK_SLOW_PENDING,
@@ -823,7 +833,9 @@ struct rtl8169_private {
        unsigned features;
 
        struct mii_if_info mii;
-       struct rtl8169_counters counters;
+       dma_addr_t counters_phys_addr;
+       struct rtl8169_counters *counters;
+       struct rtl8169_tc_offsets tc_offset;
        u32 saved_wolopts;
        u32 opts1_mask;
 
@@ -2183,65 +2195,121 @@ DECLARE_RTL_COND(rtl_counters_cond)
 {
        void __iomem *ioaddr = tp->mmio_addr;
 
-       return RTL_R32(CounterAddrLow) & CounterDump;
+       return RTL_R32(CounterAddrLow) & (CounterReset | CounterDump);
 }
 
-static void rtl8169_update_counters(struct net_device *dev)
+static bool rtl8169_do_counters(struct net_device *dev, u32 counter_cmd)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
        void __iomem *ioaddr = tp->mmio_addr;
-       struct device *d = &tp->pci_dev->dev;
-       struct rtl8169_counters *counters;
-       dma_addr_t paddr;
+       dma_addr_t paddr = tp->counters_phys_addr;
        u32 cmd;
+       bool ret;
+
+       RTL_W32(CounterAddrHigh, (u64)paddr >> 32);
+       cmd = (u64)paddr & DMA_BIT_MASK(32);
+       RTL_W32(CounterAddrLow, cmd);
+       RTL_W32(CounterAddrLow, cmd | counter_cmd);
+
+       ret = rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
+
+       RTL_W32(CounterAddrLow, 0);
+       RTL_W32(CounterAddrHigh, 0);
+
+       return ret;
+}
+
+static bool rtl8169_reset_counters(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+
+       /*
+        * Versions prior to RTL_GIGA_MAC_VER_19 don't support resetting the
+        * tally counters.
+        */
+       if (tp->mac_version < RTL_GIGA_MAC_VER_19)
+               return true;
+
+       return rtl8169_do_counters(dev, CounterReset);
+}
+
+static bool rtl8169_update_counters(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
 
        /*
         * Some chips are unable to dump tally counters when the receiver
         * is disabled.
         */
        if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0)
-               return;
+               return true;
 
-       counters = dma_alloc_coherent(d, sizeof(*counters), &paddr, GFP_KERNEL);
-       if (!counters)
-               return;
+       return rtl8169_do_counters(dev, CounterDump);
+}
 
-       RTL_W32(CounterAddrHigh, (u64)paddr >> 32);
-       cmd = (u64)paddr & DMA_BIT_MASK(32);
-       RTL_W32(CounterAddrLow, cmd);
-       RTL_W32(CounterAddrLow, cmd | CounterDump);
+static bool rtl8169_init_counter_offsets(struct net_device *dev)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       struct rtl8169_counters *counters = tp->counters;
+       bool ret = false;
 
-       if (rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000))
-               memcpy(&tp->counters, counters, sizeof(*counters));
+       /*
+        * rtl8169_init_counter_offsets is called from rtl_open.  On chip
+        * versions prior to RTL_GIGA_MAC_VER_19 the tally counters are only
+        * reset by a power cycle, while the counter values collected by the
+        * driver are reset at every driver unload/load cycle.
+        *
+        * To make sure the HW values returned by @get_stats64 match the SW
+        * values, we collect the initial values at first open(*) and use them
+        * as offsets to normalize the values returned by @get_stats64.
+        *
+        * (*) We can't call rtl8169_init_counter_offsets from rtl_init_one
+        * for the reason stated in rtl8169_update_counters; CmdRxEnb is only
+        * set at open time by rtl_hw_start.
+        */
 
-       RTL_W32(CounterAddrLow, 0);
-       RTL_W32(CounterAddrHigh, 0);
+       if (tp->tc_offset.inited)
+               return true;
+
+       /* If both, reset and update fail, propagate to caller. */
+       if (rtl8169_reset_counters(dev))
+               ret = true;
+
+       if (rtl8169_update_counters(dev))
+               ret = true;
+
+       tp->tc_offset.tx_errors = counters->tx_errors;
+       tp->tc_offset.tx_multi_collision = counters->tx_multi_collision;
+       tp->tc_offset.tx_aborted = counters->tx_aborted;
+       tp->tc_offset.inited = true;
 
-       dma_free_coherent(d, sizeof(*counters), counters, paddr);
+       return ret;
 }
 
 static void rtl8169_get_ethtool_stats(struct net_device *dev,
                                      struct ethtool_stats *stats, u64 *data)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
+       struct rtl8169_counters *counters = tp->counters;
 
        ASSERT_RTNL();
 
        rtl8169_update_counters(dev);
 
-       data[0] = le64_to_cpu(tp->counters.tx_packets);
-       data[1] = le64_to_cpu(tp->counters.rx_packets);
-       data[2] = le64_to_cpu(tp->counters.tx_errors);
-       data[3] = le32_to_cpu(tp->counters.rx_errors);
-       data[4] = le16_to_cpu(tp->counters.rx_missed);
-       data[5] = le16_to_cpu(tp->counters.align_errors);
-       data[6] = le32_to_cpu(tp->counters.tx_one_collision);
-       data[7] = le32_to_cpu(tp->counters.tx_multi_collision);
-       data[8] = le64_to_cpu(tp->counters.rx_unicast);
-       data[9] = le64_to_cpu(tp->counters.rx_broadcast);
-       data[10] = le32_to_cpu(tp->counters.rx_multicast);
-       data[11] = le16_to_cpu(tp->counters.tx_aborted);
-       data[12] = le16_to_cpu(tp->counters.tx_underun);
+       data[0] = le64_to_cpu(counters->tx_packets);
+       data[1] = le64_to_cpu(counters->rx_packets);
+       data[2] = le64_to_cpu(counters->tx_errors);
+       data[3] = le32_to_cpu(counters->rx_errors);
+       data[4] = le16_to_cpu(counters->rx_missed);
+       data[5] = le16_to_cpu(counters->align_errors);
+       data[6] = le32_to_cpu(counters->tx_one_collision);
+       data[7] = le32_to_cpu(counters->tx_multi_collision);
+       data[8] = le64_to_cpu(counters->rx_unicast);
+       data[9] = le64_to_cpu(counters->rx_broadcast);
+       data[10] = le32_to_cpu(counters->rx_multicast);
+       data[11] = le16_to_cpu(counters->tx_aborted);
+       data[12] = le16_to_cpu(counters->tx_underun);
 }
 
 static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -6013,7 +6081,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
 {
        void __iomem *ioaddr = tp->mmio_addr;
        struct pci_dev *pdev = tp->pci_dev;
-       u16 rg_saw_cnt;
+       int rg_saw_cnt;
        u32 data;
        static const struct ephy_info e_info_8168h_1[] = {
                { 0x1e, 0x0800, 0x0001 },
@@ -7367,6 +7435,9 @@ process_pkt:
                        tp->rx_stats.packets++;
                        tp->rx_stats.bytes += pkt_size;
                        u64_stats_update_end(&tp->rx_stats.syncp);
+
+                       if (skb->pkt_type == PACKET_MULTICAST)
+                               dev->stats.multicast++;
                }
 release_descriptor:
                desc->opts2 = 0;
@@ -7631,6 +7702,9 @@ static int rtl_open(struct net_device *dev)
 
        rtl_hw_start(dev);
 
+       if (!rtl8169_init_counter_offsets(dev))
+               netif_warn(tp, hw, dev, "counter reset/update failed\n");
+
        netif_start_queue(dev);
 
        rtl_unlock_work(tp);
@@ -7663,6 +7737,7 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
        void __iomem *ioaddr = tp->mmio_addr;
+       struct rtl8169_counters *counters = tp->counters;
        unsigned int start;
 
        if (netif_running(dev))
@@ -7674,7 +7749,6 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
                stats->rx_bytes = tp->rx_stats.bytes;
        } while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start));
 
-
        do {
                start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp);
                stats->tx_packets = tp->tx_stats.packets;
@@ -7688,6 +7762,24 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
        stats->rx_crc_errors    = dev->stats.rx_crc_errors;
        stats->rx_fifo_errors   = dev->stats.rx_fifo_errors;
        stats->rx_missed_errors = dev->stats.rx_missed_errors;
+       stats->multicast        = dev->stats.multicast;
+
+       /*
+        * Fetch additonal counter values missing in stats collected by driver
+        * from tally counters.
+        */
+       rtl8169_update_counters(dev);
+
+       /*
+        * Subtract values fetched during initalization.
+        * See rtl8169_init_counter_offsets for a description why we do that.
+        */
+       stats->tx_errors = le64_to_cpu(counters->tx_errors) -
+               le64_to_cpu(tp->tc_offset.tx_errors);
+       stats->collisions = le32_to_cpu(counters->tx_multi_collision) -
+               le32_to_cpu(tp->tc_offset.tx_multi_collision);
+       stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted) -
+               le16_to_cpu(tp->tc_offset.tx_aborted);
 
        return stats;
 }
@@ -7888,6 +7980,9 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
        unregister_netdev(dev);
 
+       dma_free_coherent(&tp->pci_dev->dev, sizeof(*tp->counters),
+                         tp->counters, tp->counters_phys_addr);
+
        rtl_release_firmware(tp);
 
        if (pci_dev_run_wake(pdev))
@@ -8313,9 +8408,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
 
+       tp->counters = dma_alloc_coherent (&pdev->dev, sizeof(*tp->counters),
+                                          &tp->counters_phys_addr, GFP_KERNEL);
+       if (!tp->counters) {
+               rc = -ENOMEM;
+               goto err_out_msi_4;
+       }
+
        rc = register_netdev(dev);
        if (rc < 0)
-               goto err_out_msi_4;
+               goto err_out_cnt_5;
 
        pci_set_drvdata(pdev, dev);
 
@@ -8349,6 +8451,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 out:
        return rc;
 
+err_out_cnt_5:
+       dma_free_coherent(&pdev->dev, sizeof(*tp->counters), tp->counters,
+                         tp->counters_phys_addr);
 err_out_msi_4:
        netif_napi_del(&tp->napi);
        rtl_disable_msi(pdev, tp);