#include <linux/phy.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
+#include <linux/mdio.h>
 
 #include <linux/atomic.h>
 #include <asm/io.h>
 
        schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);
 }
+
+static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
+                                   int addr)
+{
+       /* Write the desired MMD Devad */
+       bus->write(bus, addr, MII_MMD_CTRL, devad);
+
+       /* Write the desired MMD register address */
+       bus->write(bus, addr, MII_MMD_DATA, prtad);
+
+       /* Select the Function : DATA with no post increment */
+       bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+}
+
+/**
+ * phy_read_mmd_indirect - reads data from the MMD registers
+ * @bus: the target MII bus
+ * @prtad: MMD Address
+ * @devad: MMD DEVAD
+ * @addr: PHY address on the MII bus
+ *
+ * Description: it reads data from the MMD registers (clause 22 to access to
+ * clause 45) of the specified phy address.
+ * To read these register we have:
+ * 1) Write reg 13 // DEVAD
+ * 2) Write reg 14 // MMD Address
+ * 3) Write reg 13 // MMD Data Command for MMD DEVAD
+ * 3) Read  reg 14 // Read MMD data
+ */
+static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad,
+                                int addr)
+{
+       u32 ret;
+
+       mmd_phy_indirect(bus, prtad, devad, addr);
+
+       /* Read the content of the MMD's selected register */
+       ret = bus->read(bus, addr, MII_MMD_DATA);
+
+       return ret;
+}
+
+/**
+ * phy_write_mmd_indirect - writes data to the MMD registers
+ * @bus: the target MII bus
+ * @prtad: MMD Address
+ * @devad: MMD DEVAD
+ * @addr: PHY address on the MII bus
+ * @data: data to write in the MMD register
+ *
+ * Description: Write data from the MMD registers of the specified
+ * phy address.
+ * To write these register we have:
+ * 1) Write reg 13 // DEVAD
+ * 2) Write reg 14 // MMD Address
+ * 3) Write reg 13 // MMD Data Command for MMD DEVAD
+ * 3) Write reg 14 // Write MMD data
+ */
+static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad,
+                                  int addr, u32 data)
+{
+       mmd_phy_indirect(bus, prtad, devad, addr);
+
+       /* Write the data into MMD's selected register */
+       bus->write(bus, addr, MII_MMD_DATA, data);
+}
+
+static u32 phy_eee_to_adv(u16 eee_adv)
+{
+       u32 adv = 0;
+
+       if (eee_adv & MDIO_EEE_100TX)
+               adv |= ADVERTISED_100baseT_Full;
+       if (eee_adv & MDIO_EEE_1000T)
+               adv |= ADVERTISED_1000baseT_Full;
+       if (eee_adv & MDIO_EEE_10GT)
+               adv |= ADVERTISED_10000baseT_Full;
+       if (eee_adv & MDIO_EEE_1000KX)
+               adv |= ADVERTISED_1000baseKX_Full;
+       if (eee_adv & MDIO_EEE_10GKX4)
+               adv |= ADVERTISED_10000baseKX4_Full;
+       if (eee_adv & MDIO_EEE_10GKR)
+               adv |= ADVERTISED_10000baseKR_Full;
+
+       return adv;
+}
+
+static u32 phy_eee_to_supported(u16 eee_caported)
+{
+       u32 supported = 0;
+
+       if (eee_caported & MDIO_EEE_100TX)
+               supported |= SUPPORTED_100baseT_Full;
+       if (eee_caported & MDIO_EEE_1000T)
+               supported |= SUPPORTED_1000baseT_Full;
+       if (eee_caported & MDIO_EEE_10GT)
+               supported |= SUPPORTED_10000baseT_Full;
+       if (eee_caported & MDIO_EEE_1000KX)
+               supported |= SUPPORTED_1000baseKX_Full;
+       if (eee_caported & MDIO_EEE_10GKX4)
+               supported |= SUPPORTED_10000baseKX4_Full;
+       if (eee_caported & MDIO_EEE_10GKR)
+               supported |= SUPPORTED_10000baseKR_Full;
+
+       return supported;
+}
+
+static u16 phy_adv_to_eee(u32 adv)
+{
+       u16 reg = 0;
+
+       if (adv & ADVERTISED_100baseT_Full)
+               reg |= MDIO_EEE_100TX;
+       if (adv & ADVERTISED_1000baseT_Full)
+               reg |= MDIO_EEE_1000T;
+       if (adv & ADVERTISED_10000baseT_Full)
+               reg |= MDIO_EEE_10GT;
+       if (adv & ADVERTISED_1000baseKX_Full)
+               reg |= MDIO_EEE_1000KX;
+       if (adv & ADVERTISED_10000baseKX4_Full)
+               reg |= MDIO_EEE_10GKX4;
+       if (adv & ADVERTISED_10000baseKR_Full)
+               reg |= MDIO_EEE_10GKR;
+
+       return reg;
+}
+
+/**
+ * phy_init_eee - init and check the EEE feature
+ * @phydev: target phy_device struct
+ * @clk_stop_enable: PHY may stop the clock during LPI
+ *
+ * Description: it checks if the Energy-Efficient Ethernet (EEE)
+ * is supported by looking at the MMD registers 3.20 and 7.60/61
+ * and it programs the MMD register 3.0 setting the "Clock stop enable"
+ * bit if required.
+ */
+int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
+{
+       int ret = -EPROTONOSUPPORT;
+
+       /* According to 802.3az,the EEE is supported only in full duplex-mode.
+        * Also EEE feature is active when core is operating with MII, GMII
+        * or RGMII.
+        */
+       if ((phydev->duplex == DUPLEX_FULL) &&
+           ((phydev->interface == PHY_INTERFACE_MODE_MII) ||
+           (phydev->interface == PHY_INTERFACE_MODE_GMII) ||
+           (phydev->interface == PHY_INTERFACE_MODE_RGMII))) {
+               int eee_lp, eee_cap, eee_adv;
+               u32 lp, cap, adv;
+               int idx, status;
+
+               /* Read phy status to properly get the right settings */
+               status = phy_read_status(phydev);
+               if (status)
+                       return status;
+
+               /* First check if the EEE ability is supported */
+               eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE,
+                                               MDIO_MMD_PCS, phydev->addr);
+               if (eee_cap < 0)
+                       return eee_cap;
+
+               cap = phy_eee_to_supported(eee_cap);
+               if (!cap)
+                       goto eee_exit;
+
+               /* Check which link settings negotiated and verify it in
+                * the EEE advertising registers.
+                */
+               eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE,
+                                              MDIO_MMD_AN, phydev->addr);
+               if (eee_lp < 0)
+                       return eee_lp;
+
+               eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV,
+                                               MDIO_MMD_AN, phydev->addr);
+               if (eee_adv < 0)
+                       return eee_adv;
+
+               adv = phy_eee_to_adv(eee_adv);
+               lp = phy_eee_to_adv(eee_lp);
+               idx = phy_find_setting(phydev->speed, phydev->duplex);
+               if ((lp & adv & settings[idx].setting))
+                       goto eee_exit;
+
+               if (clk_stop_enable) {
+                       /* Configure the PHY to stop receiving xMII
+                        * clock while it is signaling LPI.
+                        */
+                       int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1,
+                                                       MDIO_MMD_PCS,
+                                                       phydev->addr);
+                       if (val < 0)
+                               return val;
+
+                       val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
+                       phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1,
+                                              MDIO_MMD_PCS, phydev->addr, val);
+               }
+
+               ret = 0; /* EEE supported */
+       }
+
+eee_exit:
+       return ret;
+}
+EXPORT_SYMBOL(phy_init_eee);
+
+/**
+ * phy_get_eee_err - report the EEE wake error count
+ * @phydev: target phy_device struct
+ *
+ * Description: it is to report the number of time where the PHY
+ * failed to complete its normal wake sequence.
+ */
+int phy_get_eee_err(struct phy_device *phydev)
+{
+       return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR,
+                                    MDIO_MMD_PCS, phydev->addr);
+
+}
+EXPORT_SYMBOL(phy_get_eee_err);
+
+/**
+ * phy_ethtool_get_eee - get EEE supported and status
+ * @phydev: target phy_device struct
+ * @data: ethtool_eee data
+ *
+ * Description: it reportes the Supported/Advertisement/LP Advertisement
+ * capabilities.
+ */
+int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
+{
+       int val;
+
+       /* Get Supported EEE */
+       val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE,
+                                   MDIO_MMD_PCS, phydev->addr);
+       if (val < 0)
+               return val;
+       data->supported = phy_eee_to_supported(val);
+
+       /* Get advertisement EEE */
+       val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV,
+                                   MDIO_MMD_AN, phydev->addr);
+       if (val < 0)
+               return val;
+       data->advertised = phy_eee_to_adv(val);
+
+       /* Get LP advertisement EEE */
+       val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE,
+                                   MDIO_MMD_AN, phydev->addr);
+       if (val < 0)
+               return val;
+       data->lp_advertised = phy_eee_to_adv(val);
+
+       return 0;
+}
+EXPORT_SYMBOL(phy_ethtool_get_eee);
+
+/**
+ * phy_ethtool_set_eee - set EEE supported and status
+ * @phydev: target phy_device struct
+ * @data: ethtool_eee data
+ *
+ * Description: it is to program the Advertisement EEE register.
+ */
+int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
+{
+       int val;
+
+       val = phy_adv_to_eee(data->advertised);
+       phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN,
+                              phydev->addr, val);
+
+       return 0;
+}
+EXPORT_SYMBOL(phy_ethtool_set_eee);
 
 #define MDIO_PKGID2            15
 #define MDIO_AN_ADVERTISE      16      /* AN advertising (base page) */
 #define MDIO_AN_LPA            19      /* AN LP abilities (base page) */
+#define MDIO_PCS_EEE_ABLE      20      /* EEE Capability register */
+#define MDIO_PCS_EEE_WK_ERR    22      /* EEE wake error counter */
 #define MDIO_PHYXS_LNSTAT      24      /* PHY XGXS lane state */
+#define MDIO_AN_EEE_ADV                60      /* EEE advertisement */
+#define MDIO_AN_EEE_LPABLE     61      /* EEE link partner ability */
 
 /* Media-dependent registers. */
 #define MDIO_PMA_10GBT_SWAPPOL 130     /* 10GBASE-T pair swap & polarity */
 #define MDIO_PCS_10GBRT_STAT2  33      /* 10GBASE-R/-T PCS status 2 */
 #define MDIO_AN_10GBT_CTRL     32      /* 10GBASE-T auto-negotiation control */
 #define MDIO_AN_10GBT_STAT     33      /* 10GBASE-T auto-negotiation status */
-#define MDIO_AN_EEE_ADV                60      /* EEE advertisement */
 
 /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */
 #define MDIO_PMA_LASI_RXCTRL   0x9000  /* RX_ALARM control */
 #define MDIO_AN_CTRL1_RESTART          BMCR_ANRESTART
 #define MDIO_AN_CTRL1_ENABLE           BMCR_ANENABLE
 #define MDIO_AN_CTRL1_XNP              0x2000  /* Enable extended next page */
+#define MDIO_PCS_CTRL1_CLKSTOP_EN      0x400   /* Stop the clock during LPI */
 
 /* 10 Gb/s */
 #define MDIO_CTRL1_SPEED10G            (MDIO_CTRL1_SPEEDSELEXT | 0x00)
 #define MDIO_AN_10GBT_STAT_MS          0x4000  /* Master/slave config */
 #define MDIO_AN_10GBT_STAT_MSFLT       0x8000  /* Master/slave config fault */
 
-/* AN EEE Advertisement register. */
-#define MDIO_AN_EEE_ADV_100TX          0x0002  /* Advertise 100TX EEE cap */
-#define MDIO_AN_EEE_ADV_1000T          0x0004  /* Advertise 1000T EEE cap */
+/* EEE Supported/Advertisement/LP Advertisement registers.
+ *
+ * EEE capability Register (3.20), Advertisement (7.60) and
+ * Link partner ability (7.61) registers have and can use the same identical
+ * bit masks.
+ */
+#define MDIO_AN_EEE_ADV_100TX  0x0002  /* Advertise 100TX EEE cap */
+#define MDIO_AN_EEE_ADV_1000T  0x0004  /* Advertise 1000T EEE cap */
+/* Note: the two defines above can be potentially used by the user-land
+ * and cannot remove them now.
+ * So, we define the new generic MDIO_EEE_100TX and MDIO_EEE_1000T macros
+ * using the previous ones (that can be considered obsolete).
+ */
+#define MDIO_EEE_100TX         MDIO_AN_EEE_ADV_100TX   /* 100TX EEE cap */
+#define MDIO_EEE_1000T         MDIO_AN_EEE_ADV_1000T   /* 1000T EEE cap */
+#define MDIO_EEE_10GT          0x0008  /* 10GT EEE cap */
+#define MDIO_EEE_1000KX                0x0010  /* 1000KX EEE cap */
+#define MDIO_EEE_10GKX4                0x0020  /* 10G KX4 EEE cap */
+#define MDIO_EEE_10GKR         0x0040  /* 10G KR EEE cap */
 
 /* LASI RX_ALARM control/status registers. */
 #define MDIO_PMA_LASI_RX_PHYXSLFLT     0x0001  /* PHY XS RX local fault */