MODULE_AUTHOR("Andy Fleming");
 MODULE_LICENSE("GPL");
 
+struct marvell_hw_stat {
+       const char *string;
+       u8 page;
+       u8 reg;
+       u8 bits;
+};
+
+static struct marvell_hw_stat marvell_hw_stats[] = {
+       { "phy_receive_errors", 0, 21, 16},
+       { "phy_idle_errors", 0, 10, 8 },
+};
+
+struct marvell_priv {
+       u64 stats[ARRAY_SIZE(marvell_hw_stats)];
+};
+
 static int marvell_ack_interrupt(struct phy_device *phydev)
 {
        int err;
        return 0;
 }
 
+static int marvell_get_sset_count(struct phy_device *phydev)
+{
+       return ARRAY_SIZE(marvell_hw_stats);
+}
+
+static void marvell_get_strings(struct phy_device *phydev, u8 *data)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
+               memcpy(data + i * ETH_GSTRING_LEN,
+                      marvell_hw_stats[i].string, ETH_GSTRING_LEN);
+       }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX              (u64)(~((u64)0))
+#endif
+static u64 marvell_get_stat(struct phy_device *phydev, int i)
+{
+       struct marvell_hw_stat stat = marvell_hw_stats[i];
+       struct marvell_priv *priv = phydev->priv;
+       int err, oldpage;
+       u64 val;
+
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+                       stat.page);
+       if (err < 0)
+               return UINT64_MAX;
+
+       val = phy_read(phydev, stat.reg);
+       if (val < 0) {
+               val = UINT64_MAX;
+       } else {
+               val = val & ((1 << stat.bits) - 1);
+               priv->stats[i] += val;
+               val = priv->stats[i];
+       }
+
+       phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+
+       return val;
+}
+
+static void marvell_get_stats(struct phy_device *phydev,
+                             struct ethtool_stats *stats, u64 *data)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
+               data[i] = marvell_get_stat(phydev, i);
+}
+
+static int marvell_probe(struct phy_device *phydev)
+{
+       struct marvell_priv *priv;
+
+       priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
 static struct phy_driver marvell_drivers[] = {
        {
                .phy_id = MARVELL_PHY_ID_88E1101,
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1101",
                .features = PHY_GBIT_FEATURES,
+               .probe = marvell_probe,
                .flags = PHY_HAS_INTERRUPT,
                .config_aneg = &marvell_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1112",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1111",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
                .read_status = &marvell_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1118",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1118_config_init,
                .config_aneg = &m88e1118_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = {.owner = THIS_MODULE,},
        },
        {
                .name = "Marvell 88E1121R",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_aneg = &m88e1121_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .did_interrupt = &m88e1121_did_interrupt,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1318S",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_aneg = &m88e1318_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .set_wol = &m88e1318_set_wol,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1145",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1145_config_init,
                .config_aneg = &marvell_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1149R",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1149_config_init,
                .config_aneg = &m88e1118_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1240",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1116R",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_init = &m88e1116r_config_init,
                .config_aneg = &genphy_config_aneg,
                .read_status = &genphy_read_status,
                .config_intr = &marvell_config_intr,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1510",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_aneg = &m88e1510_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .did_interrupt = &m88e1121_did_interrupt,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E1540",
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_aneg = &m88e1510_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .did_interrupt = &m88e1121_did_interrupt,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
        {
                .name = "Marvell 88E3016",
                .features = PHY_BASIC_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
+               .probe = marvell_probe,
                .config_aneg = &genphy_config_aneg,
                .config_init = &m88e3016_config_init,
                .aneg_done = &marvell_aneg_done,
                .did_interrupt = &m88e1121_did_interrupt,
                .resume = &genphy_resume,
                .suspend = &genphy_suspend,
+               .get_sset_count = marvell_get_sset_count,
+               .get_strings = marvell_get_strings,
+               .get_stats = marvell_get_stats,
                .driver = { .owner = THIS_MODULE },
        },
 };