#define   ADIN1300_AUTO_MDI_EN                 BIT(10)
 #define   ADIN1300_MAN_MDIX_EN                 BIT(9)
 
+#define ADIN1300_RX_ERR_CNT                    0x0014
+
 #define ADIN1300_PHY_CTRL2                     0x0016
 #define   ADIN1300_DOWNSPEED_AN_100_EN         BIT(11)
 #define   ADIN1300_DOWNSPEED_AN_10_EN          BIT(10)
        { MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR,    ADIN1300_LPI_WAKE_ERR_CNT_REG },
 };
 
+struct adin_hw_stat {
+       const char *string;
+       u16 reg1;
+       u16 reg2;
+};
+
+static struct adin_hw_stat adin_hw_stats[] = {
+       { "total_frames_checked_count",         0x940A, 0x940B }, /* hi + lo */
+       { "length_error_frames_count",          0x940C },
+       { "alignment_error_frames_count",       0x940D },
+       { "symbol_error_count",                 0x940E },
+       { "oversized_frames_count",             0x940F },
+       { "undersized_frames_count",            0x9410 },
+       { "odd_nibble_frames_count",            0x9411 },
+       { "odd_preamble_packet_count",          0x9412 },
+       { "dribble_bits_frames_count",          0x9413 },
+       { "false_carrier_events_count",         0x9414 },
+};
+
+/**
+ * struct adin_priv - ADIN PHY driver private data
+ * stats               statistic counters for the PHY
+ */
+struct adin_priv {
+       u64                     stats[ARRAY_SIZE(adin_hw_stats)];
+};
+
 static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
 {
        size_t i;
        return rc < 0 ? rc : 0;
 }
 
+static int adin_get_sset_count(struct phy_device *phydev)
+{
+       return ARRAY_SIZE(adin_hw_stats);
+}
+
+static void adin_get_strings(struct phy_device *phydev, u8 *data)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++) {
+               strlcpy(&data[i * ETH_GSTRING_LEN],
+                       adin_hw_stats[i].string, ETH_GSTRING_LEN);
+       }
+}
+
+static int adin_read_mmd_stat_regs(struct phy_device *phydev,
+                                  struct adin_hw_stat *stat,
+                                  u32 *val)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg1);
+       if (ret < 0)
+               return ret;
+
+       *val = (ret & 0xffff);
+
+       if (stat->reg2 == 0)
+               return 0;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, stat->reg2);
+       if (ret < 0)
+               return ret;
+
+       *val <<= 16;
+       *val |= (ret & 0xffff);
+
+       return 0;
+}
+
+static u64 adin_get_stat(struct phy_device *phydev, int i)
+{
+       struct adin_hw_stat *stat = &adin_hw_stats[i];
+       struct adin_priv *priv = phydev->priv;
+       u32 val;
+       int ret;
+
+       if (stat->reg1 > 0x1f) {
+               ret = adin_read_mmd_stat_regs(phydev, stat, &val);
+               if (ret < 0)
+                       return (u64)(~0);
+       } else {
+               ret = phy_read(phydev, stat->reg1);
+               if (ret < 0)
+                       return (u64)(~0);
+               val = (ret & 0xffff);
+       }
+
+       priv->stats[i] += val;
+
+       return priv->stats[i];
+}
+
+static void adin_get_stats(struct phy_device *phydev,
+                          struct ethtool_stats *stats, u64 *data)
+{
+       int i, rc;
+
+       /* latch copies of all the frame-checker counters */
+       rc = phy_read(phydev, ADIN1300_RX_ERR_CNT);
+       if (rc < 0)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++)
+               data[i] = adin_get_stat(phydev, i);
+}
+
+static int adin_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct adin_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
 static struct phy_driver adin_driver[] = {
        {
                PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200),
                .name           = "ADIN1200",
+               .probe          = adin_probe,
                .config_init    = adin_config_init,
                .soft_reset     = adin_soft_reset,
                .config_aneg    = adin_config_aneg,
                .set_tunable    = adin_set_tunable,
                .ack_interrupt  = adin_phy_ack_intr,
                .config_intr    = adin_phy_config_intr,
+               .get_sset_count = adin_get_sset_count,
+               .get_strings    = adin_get_strings,
+               .get_stats      = adin_get_stats,
                .resume         = genphy_resume,
                .suspend        = genphy_suspend,
                .read_mmd       = adin_read_mmd,
        {
                PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300),
                .name           = "ADIN1300",
+               .probe          = adin_probe,
                .config_init    = adin_config_init,
                .soft_reset     = adin_soft_reset,
                .config_aneg    = adin_config_aneg,
                .set_tunable    = adin_set_tunable,
                .ack_interrupt  = adin_phy_ack_intr,
                .config_intr    = adin_phy_config_intr,
+               .get_sset_count = adin_get_sset_count,
+               .get_strings    = adin_get_strings,
+               .get_stats      = adin_get_stats,
                .resume         = genphy_resume,
                .suspend        = genphy_suspend,
                .read_mmd       = adin_read_mmd,