int (*module_eeprom)(struct phy_device *dev,
                             struct ethtool_eeprom *ee, u8 *data);
 
+       /* Get statistics from the phy using ethtool */
+       int (*get_sset_count)(struct phy_device *dev);
+       void (*get_strings)(struct phy_device *dev, u8 *data);
+       void (*get_stats)(struct phy_device *dev,
+                         struct ethtool_stats *stats, u64 *data);
+
        struct device_driver driver;
 };
 #define to_phy_driver(d) container_of(d, struct phy_driver, driver)
 
  *     now deprecated
  * @ETH_SS_FEATURES: Device feature names
  * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
+ * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
  */
 enum ethtool_stringset {
        ETH_SS_TEST             = 0,
        ETH_SS_FEATURES,
        ETH_SS_RSS_HASH_FUNCS,
        ETH_SS_TUNABLES,
+       ETH_SS_PHY_STATS,
 };
 
 /**
 #define ETHTOOL_SRSSH          0x00000047 /* Set RX flow hash configuration */
 #define ETHTOOL_GTUNABLE       0x00000048 /* Get tunable configuration */
 #define ETHTOOL_STUNABLE       0x00000049 /* Set tunable configuration */
+#define ETHTOOL_GPHYSTATS      0x0000004a /* get PHY-specific statistics */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET         ETHTOOL_GSET
 
        return ret;
 }
 
+static int phy_get_sset_count(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->drv->get_sset_count &&
+           phydev->drv->get_strings &&
+           phydev->drv->get_stats) {
+               mutex_lock(&phydev->lock);
+               ret = phydev->drv->get_sset_count(phydev);
+               mutex_unlock(&phydev->lock);
+
+               return ret;
+       }
+
+       return -EOPNOTSUPP;
+}
+
 static int __ethtool_get_sset_count(struct net_device *dev, int sset)
 {
        const struct ethtool_ops *ops = dev->ethtool_ops;
        if (sset == ETH_SS_TUNABLES)
                return ARRAY_SIZE(tunable_strings);
 
+       if (sset == ETH_SS_PHY_STATS) {
+               if (dev->phydev)
+                       return phy_get_sset_count(dev->phydev);
+               else
+                       return -EOPNOTSUPP;
+       }
+
        if (ops->get_sset_count && ops->get_strings)
                return ops->get_sset_count(dev, sset);
        else
                       sizeof(rss_hash_func_strings));
        else if (stringset == ETH_SS_TUNABLES)
                memcpy(data, tunable_strings, sizeof(tunable_strings));
-       else
+       else if (stringset == ETH_SS_PHY_STATS) {
+               struct phy_device *phydev = dev->phydev;
+
+               if (phydev) {
+                       mutex_lock(&phydev->lock);
+                       phydev->drv->get_strings(phydev, data);
+                       mutex_unlock(&phydev->lock);
+               } else {
+                       return;
+               }
+       } else
                /* ops->get_strings is valid because checked earlier */
                ops->get_strings(dev, stringset, data);
 }
        return ret;
 }
 
+static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+{
+       struct ethtool_stats stats;
+       struct phy_device *phydev = dev->phydev;
+       u64 *data;
+       int ret, n_stats;
+
+       if (!phydev)
+               return -EOPNOTSUPP;
+
+       n_stats = phy_get_sset_count(phydev);
+
+       if (n_stats < 0)
+               return n_stats;
+       WARN_ON(n_stats == 0);
+
+       if (copy_from_user(&stats, useraddr, sizeof(stats)))
+               return -EFAULT;
+
+       stats.n_stats = n_stats;
+       data = kmalloc_array(n_stats, sizeof(u64), GFP_USER);
+       if (!data)
+               return -ENOMEM;
+
+       mutex_lock(&phydev->lock);
+       phydev->drv->get_stats(phydev, &stats, data);
+       mutex_unlock(&phydev->lock);
+
+       ret = -EFAULT;
+       if (copy_to_user(useraddr, &stats, sizeof(stats)))
+               goto out;
+       useraddr += sizeof(stats);
+       if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
+               goto out;
+       ret = 0;
+
+ out:
+       kfree(data);
+       return ret;
+}
+
 static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_perm_addr epaddr;
        case ETHTOOL_GSSET_INFO:
        case ETHTOOL_GSTRINGS:
        case ETHTOOL_GSTATS:
+       case ETHTOOL_GPHYSTATS:
        case ETHTOOL_GTSO:
        case ETHTOOL_GPERMADDR:
        case ETHTOOL_GUFO:
        case ETHTOOL_STUNABLE:
                rc = ethtool_set_tunable(dev, useraddr);
                break;
+       case ETHTOOL_GPHYSTATS:
+               rc = ethtool_get_phy_stats(dev, useraddr);
+               break;
        default:
                rc = -EOPNOTSUPP;
        }