net: phy: broadcom: Add support code for reading PHY counters
authorFlorian Fainelli <f.fainelli@gmail.com>
Tue, 29 Nov 2016 17:57:17 +0000 (09:57 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 30 Nov 2016 15:22:27 +0000 (10:22 -0500)
Broadcom PHYs expose a number of PHY error counters: receive errors,
false carrier sense, SerDes BER count, local and remote receive errors.
Add support code to allow retrieving these error counters. Since the
Broadcom PHY library code is used by several drivers, make it possible
for them to specify the storage for the software copy of the statistics.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/bcm-phy-lib.c
drivers/net/phy/bcm-phy-lib.h
include/linux/brcmphy.h

index 3156ce6d5861b8e8fdbc5add1c8f725a6dea5d62..ab9ad689617c78d19b21ad8f35b5f1887c483b1c 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mdio.h>
 #include <linux/module.h>
 #include <linux/phy.h>
+#include <linux/ethtool.h>
 
 #define MII_BCM_CHANNEL_WIDTH     0x2000
 #define BCM_CL45VEN_EEE_ADV       0x3c
@@ -317,6 +318,75 @@ int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
 }
 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
 
+struct bcm_phy_hw_stat {
+       const char *string;
+       u8 reg;
+       u8 shift;
+       u8 bits;
+};
+
+/* Counters freeze at either 0xffff or 0xff, better than nothing */
+static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
+       { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
+       { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
+       { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
+       { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
+       { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
+};
+
+int bcm_phy_get_sset_count(struct phy_device *phydev)
+{
+       return ARRAY_SIZE(bcm_phy_hw_stats);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
+
+void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
+               memcpy(data + i * ETH_GSTRING_LEN,
+                      bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
+
+#ifndef UINT64_MAX
+#define UINT64_MAX              (u64)(~((u64)0))
+#endif
+
+/* Caller is supposed to provide appropriate storage for the library code to
+ * access the shadow copy
+ */
+static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
+                           unsigned int i)
+{
+       struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
+       int val;
+       u64 ret;
+
+       val = phy_read(phydev, stat.reg);
+       if (val < 0) {
+               ret = UINT64_MAX;
+       } else {
+               val >>= stat.shift;
+               val = val & ((1 << stat.bits) - 1);
+               shadow[i] += val;
+               ret = shadow[i];
+       }
+
+       return ret;
+}
+
+void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
+                      struct ethtool_stats *stats, u64 *data)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
+               data[i] = bcm_phy_get_stat(phydev, shadow, i);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
+
 MODULE_DESCRIPTION("Broadcom PHY Library");
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Broadcom Corporation");
index a117f657c6d781265f396d92e7b1f1296fb9f108..7c73808cbbded22bb01b60ef2b616456267f99cc 100644 (file)
@@ -42,4 +42,9 @@ int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count);
 
 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count);
 
+int bcm_phy_get_sset_count(struct phy_device *phydev);
+void bcm_phy_get_strings(struct phy_device *phydev, u8 *data);
+void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
+                      struct ethtool_stats *stats, u64 *data);
+
 #endif /* _LINUX_BCM_PHY_LIB_H */
index f9f8aaf9c943fe4b6d7386be34d4e47e6301aa97..4f7d8be9ddbf8de2fc0e657764e9dfe515852e3e 100644 (file)
 #define LPI_FEATURE_EN_DIG1000X                0x4000
 
 /* Core register definitions*/
+#define MII_BRCM_CORE_BASE12   0x12
+#define MII_BRCM_CORE_BASE13   0x13
+#define MII_BRCM_CORE_BASE14   0x14
 #define MII_BRCM_CORE_BASE1E   0x1E
 #define MII_BRCM_CORE_EXPB0    0xB0
 #define MII_BRCM_CORE_EXPB1    0xB1