]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/usb/lan78xx.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[karo-tx-linux.git] / drivers / net / usb / lan78xx.c
index a39518fc93aadf82918fd944dcf88c6de5321f48..226668ead0d8c1ba9e6a0edf906a2122a5e74d2e 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
-#include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/crc32.h>
 #include <linux/signal.h>
 #include <linux/ipv6.h>
 #include <linux/mdio.h>
 #include <net/ip6_checksum.h>
+#include <linux/microchipphy.h>
 #include "lan78xx.h"
 
 #define DRIVER_AUTHOR  "WOOJUNG HUH <woojung.huh@microchip.com>"
 #define DRIVER_DESC    "LAN78XX USB 3.0 Gigabit Ethernet Devices"
 #define DRIVER_NAME    "lan78xx"
-#define DRIVER_VERSION "1.0.0"
+#define DRIVER_VERSION "1.0.1"
 
 #define TX_TIMEOUT_JIFFIES             (5 * HZ)
 #define THROTTLE_JIFFIES               (HZ / 8)
@@ -57,7 +57,6 @@
 #define DEFAULT_RX_CSUM_ENABLE         (true)
 #define DEFAULT_TSO_CSUM_ENABLE                (true)
 #define DEFAULT_VLAN_FILTER_ENABLE     (true)
-#define INTERNAL_PHY_ID                        (2)     /* 2: GMII */
 #define TX_OVERHEAD                    (8)
 #define RXW_PADDING                    2
 
@@ -275,10 +274,12 @@ struct lan78xx_net {
        struct timer_list       delay;
 
        unsigned long           data[5];
-       struct mii_if_info      mii;
 
        int                     link_on;
        u8                      mdix_ctrl;
+
+       u32                     devid;
+       struct mii_bus          *mdiobus;
 };
 
 /* use ethtool to change the level for any given device */
@@ -411,222 +412,6 @@ static inline u32 mii_access(int id, int index, int read)
        return ret;
 }
 
-static int lan78xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       ret = usb_autopm_get_interface(dev->intf);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set the address, index & direction (read from PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       idx &= dev->mii.reg_num_mask;
-       addr = mii_access(phy_id, idx, MII_READ);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       ret = lan78xx_read_reg(dev, MII_DATA, &val);
-
-       ret = (int)(val & 0xFFFF);
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-       return ret;
-}
-
-static void lan78xx_mdio_write(struct net_device *netdev, int phy_id,
-                              int idx, int regval)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       if (usb_autopm_get_interface(dev->intf) < 0)
-               return;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       val = regval;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       /* set the address, index & direction (write to PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       idx &= dev->mii.reg_num_mask;
-       addr = mii_access(phy_id, idx, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-}
-
-static void lan78xx_mmd_write(struct net_device *netdev, int phy_id,
-                             int mmddev, int mmdidx, int regval)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       if (usb_autopm_get_interface(dev->intf) < 0)
-               return;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       mmddev &= 0x1F;
-
-       /* set up device address for MMD */
-       ret = lan78xx_write_reg(dev, MII_DATA, mmddev);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register of MMD */
-       val = mmdidx;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register data for MMD */
-       val = PHY_MMD_CTRL_OP_DNI_ | mmddev;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* write to MMD */
-       val = regval;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-}
-
-static int lan78xx_mmd_read(struct net_device *netdev, int phy_id,
-                           int mmddev, int mmdidx)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       ret = usb_autopm_get_interface(dev->intf);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set up device address for MMD */
-       ret = lan78xx_write_reg(dev, MII_DATA, mmddev);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register of MMD */
-       val = mmdidx;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register data for MMD */
-       val = PHY_MMD_CTRL_OP_DNI_ | mmddev;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set the address, index & direction (read from PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_READ);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* read from MMD */
-       ret = lan78xx_read_reg(dev, MII_DATA, &val);
-
-       ret = (int)(val & 0xFFFF);
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-       return ret;
-}
-
 static int lan78xx_wait_eeprom(struct lan78xx_net *dev)
 {
        unsigned long start_time = jiffies;
@@ -1047,14 +832,13 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
 
 static int lan78xx_link_reset(struct lan78xx_net *dev)
 {
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = dev->net->phydev;
        struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
        int ladv, radv, ret;
        u32 buf;
 
        /* clear PHY interrupt status */
-       /* VTSE PHY */
-       ret = lan78xx_mdio_read(dev->net, mii->phy_id, PHY_VTSE_INT_STS);
+       ret = phy_read(phydev, LAN88XX_INT_STS);
        if (unlikely(ret < 0))
                return -EIO;
 
@@ -1063,7 +847,9 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
        if (unlikely(ret < 0))
                return -EIO;
 
-       if (!mii_link_ok(mii) && dev->link_on) {
+       phy_read_status(phydev);
+
+       if (!phydev->link && dev->link_on) {
                dev->link_on = false;
                netif_carrier_off(dev->net);
 
@@ -1075,13 +861,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
                ret = lan78xx_write_reg(dev, MAC_CR, buf);
                if (unlikely(ret < 0))
                        return -EIO;
-       } else if (mii_link_ok(mii) && !dev->link_on) {
+       } else if (phydev->link && !dev->link_on) {
                dev->link_on = true;
 
-               mii_check_media(mii, 1, 1);
-               mii_ethtool_gset(&dev->mii, &ecmd);
+               phy_ethtool_gset(phydev, &ecmd);
 
-               mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS);
+               ret = phy_read(phydev, LAN88XX_INT_STS);
 
                if (dev->udev->speed == USB_SPEED_SUPER) {
                        if (ethtool_cmd_speed(&ecmd) == 1000) {
@@ -1102,11 +887,11 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
                        }
                }
 
-               ladv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);
+               ladv = phy_read(phydev, MII_ADVERTISE);
                if (ladv < 0)
                        return ladv;
 
-               radv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_LPA);
+               radv = phy_read(phydev, MII_LPA);
                if (radv < 0)
                        return radv;
 
@@ -1279,6 +1064,8 @@ static int lan78xx_set_wol(struct net_device *netdev,
 
        device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts);
 
+       phy_ethtool_set_wol(netdev->phydev, wol);
+
        usb_autopm_put_interface(dev->intf);
 
        return ret;
@@ -1287,49 +1074,39 @@ static int lan78xx_set_wol(struct net_device *netdev,
 static int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata)
 {
        struct lan78xx_net *dev = netdev_priv(net);
+       struct phy_device *phydev = net->phydev;
        int ret;
        u32 buf;
-       u32 adv, lpadv;
 
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
+       ret = phy_ethtool_get_eee(phydev, edata);
+       if (ret < 0)
+               goto exit;
+
        ret = lan78xx_read_reg(dev, MAC_CR, &buf);
        if (buf & MAC_CR_EEE_EN_) {
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT);
-               adv = mmd_eee_adv_to_ethtool_adv_t(buf);
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT);
-               lpadv = mmd_eee_adv_to_ethtool_adv_t(buf);
-
                edata->eee_enabled = true;
-               edata->supported = true;
-               edata->eee_active = !!(adv & lpadv);
-               edata->advertised = adv;
-               edata->lp_advertised = lpadv;
+               edata->eee_active = !!(edata->advertised &
+                                      edata->lp_advertised);
                edata->tx_lpi_enabled = true;
                /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */
                ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf);
                edata->tx_lpi_timer = buf;
        } else {
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT);
-               lpadv = mmd_eee_adv_to_ethtool_adv_t(buf);
-
                edata->eee_enabled = false;
                edata->eee_active = false;
-               edata->supported = false;
-               edata->advertised = 0;
-               edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(lpadv);
                edata->tx_lpi_enabled = false;
                edata->tx_lpi_timer = 0;
        }
 
+       ret = 0;
+exit:
        usb_autopm_put_interface(dev->intf);
 
-       return 0;
+       return ret;
 }
 
 static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
@@ -1347,9 +1124,10 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
                buf |= MAC_CR_EEE_EN_;
                ret = lan78xx_write_reg(dev, MAC_CR, buf);
 
-               buf = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
-               lan78xx_mmd_write(dev->net, dev->mii.phy_id,
-                                 PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT, buf);
+               phy_ethtool_set_eee(net->phydev, edata);
+
+               buf = (u32)edata->tx_lpi_timer;
+               ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf);
        } else {
                ret = lan78xx_read_reg(dev, MAC_CR, &buf);
                buf &= ~MAC_CR_EEE_EN_;
@@ -1363,19 +1141,14 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
 
 static u32 lan78xx_get_link(struct net_device *net)
 {
-       struct lan78xx_net *dev = netdev_priv(net);
+       phy_read_status(net->phydev);
 
-       return mii_link_ok(&dev->mii);
+       return net->phydev->link;
 }
 
 int lan78xx_nway_reset(struct net_device *net)
 {
-       struct lan78xx_net *dev = netdev_priv(net);
-
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
-       return mii_nway_restart(&dev->mii);
+       return phy_start_aneg(net->phydev);
 }
 
 static void lan78xx_get_drvinfo(struct net_device *net,
@@ -1402,36 +1175,78 @@ static void lan78xx_set_msglevel(struct net_device *net, u32 level)
        dev->msg_enable = level;
 }
 
+static int lan78xx_get_mdix_status(struct net_device *net)
+{
+       struct phy_device *phydev = net->phydev;
+       int buf;
+
+       phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1);
+       buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+       phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0);
+
+       return buf;
+}
+
+static void lan78xx_set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
+{
+       struct lan78xx_net *dev = netdev_priv(net);
+       struct phy_device *phydev = net->phydev;
+       int buf;
+
+       if (mdix_ctrl == ETH_TP_MDI) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_MDI_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       } else if (mdix_ctrl == ETH_TP_MDI_X) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_MDI_X_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       } else if (mdix_ctrl == ETH_TP_MDI_AUTO) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       }
+       dev->mdix_ctrl = mdix_ctrl;
+}
+
 static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
        struct lan78xx_net *dev = netdev_priv(net);
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = net->phydev;
        int ret;
        int buf;
 
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
-       ret = mii_ethtool_gset(&dev->mii, cmd);
+       ret = phy_ethtool_gset(phydev, cmd);
 
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
-       buf = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL);
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0);
+       buf = lan78xx_get_mdix_status(net);
 
-       buf &= PHY_EXT_MODE_CTRL_MDIX_MASK_;
-       if (buf == PHY_EXT_MODE_CTRL_AUTO_MDIX_) {
+       buf &= LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+       if (buf == LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_) {
                cmd->eth_tp_mdix = ETH_TP_MDI_AUTO;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
-       } else if (buf == PHY_EXT_MODE_CTRL_MDI_) {
+       } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_) {
                cmd->eth_tp_mdix = ETH_TP_MDI;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI;
-       } else if (buf == PHY_EXT_MODE_CTRL_MDI_X_) {
+       } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_X_) {
                cmd->eth_tp_mdix = ETH_TP_MDI_X;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X;
        }
@@ -1444,70 +1259,27 @@ static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
        struct lan78xx_net *dev = netdev_priv(net);
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = net->phydev;
        int ret = 0;
        int temp;
 
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
        if (dev->mdix_ctrl != cmd->eth_tp_mdix_ctrl) {
-               if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_MDI_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_X) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_MDI_X_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               }
+               lan78xx_set_mdix_status(net, cmd->eth_tp_mdix_ctrl);
        }
 
        /* change speed & duplex */
-       ret = mii_ethtool_sset(&dev->mii, cmd);
+       ret = phy_ethtool_sset(phydev, cmd);
 
        if (!cmd->autoneg) {
                /* force link down */
-               temp = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
-               mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR,
-                               temp | BMCR_LOOPBACK);
+               temp = phy_read(phydev, MII_BMCR);
+               phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK);
                mdelay(1);
-               mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, temp);
+               phy_write(phydev, MII_BMCR, temp);
        }
 
        usb_autopm_put_interface(dev->intf);
@@ -1537,12 +1309,10 @@ static const struct ethtool_ops lan78xx_ethtool_ops = {
 
 static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
 {
-       struct lan78xx_net *dev = netdev_priv(netdev);
-
        if (!netif_running(netdev))
                return -EINVAL;
 
-       return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+       return phy_mii_ioctl(netdev->phydev, rq, cmd);
 }
 
 static void lan78xx_init_mac_address(struct lan78xx_net *dev)
@@ -1598,53 +1368,183 @@ static void lan78xx_init_mac_address(struct lan78xx_net *dev)
        ether_addr_copy(dev->net->dev_addr, addr);
 }
 
-static void lan78xx_mii_init(struct lan78xx_net *dev)
+/* MDIO read and write wrappers for phylib */
+static int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
 {
-       /* Initialize MII structure */
-       dev->mii.dev = dev->net;
-       dev->mii.mdio_read = lan78xx_mdio_read;
-       dev->mii.mdio_write = lan78xx_mdio_write;
-       dev->mii.phy_id_mask = 0x1f;
-       dev->mii.reg_num_mask = 0x1f;
-       dev->mii.phy_id = INTERNAL_PHY_ID;
-       dev->mii.supports_gmii = true;
+       struct lan78xx_net *dev = bus->priv;
+       u32 val, addr;
+       int ret;
+
+       ret = usb_autopm_get_interface(dev->intf);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&dev->phy_mutex);
+
+       /* confirm MII not busy */
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       /* set the address, index & direction (read from PHY) */
+       addr = mii_access(phy_id, idx, MII_READ);
+       ret = lan78xx_write_reg(dev, MII_ACC, addr);
+
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       ret = lan78xx_read_reg(dev, MII_DATA, &val);
+
+       ret = (int)(val & 0xFFFF);
+
+done:
+       mutex_unlock(&dev->phy_mutex);
+       usb_autopm_put_interface(dev->intf);
+       return ret;
+}
+
+static int lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx,
+                                u16 regval)
+{
+       struct lan78xx_net *dev = bus->priv;
+       u32 val, addr;
+       int ret;
+
+       ret = usb_autopm_get_interface(dev->intf);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&dev->phy_mutex);
+
+       /* confirm MII not busy */
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       val = (u32)regval;
+       ret = lan78xx_write_reg(dev, MII_DATA, val);
+
+       /* set the address, index & direction (write to PHY) */
+       addr = mii_access(phy_id, idx, MII_WRITE);
+       ret = lan78xx_write_reg(dev, MII_ACC, addr);
+
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+done:
+       mutex_unlock(&dev->phy_mutex);
+       usb_autopm_put_interface(dev->intf);
+       return 0;
+}
+
+static int lan78xx_mdio_init(struct lan78xx_net *dev)
+{
+       int ret;
+       int i;
+
+       dev->mdiobus = mdiobus_alloc();
+       if (!dev->mdiobus) {
+               netdev_err(dev->net, "can't allocate MDIO bus\n");
+               return -ENOMEM;
+       }
+
+       dev->mdiobus->priv = (void *)dev;
+       dev->mdiobus->read = lan78xx_mdiobus_read;
+       dev->mdiobus->write = lan78xx_mdiobus_write;
+       dev->mdiobus->name = "lan78xx-mdiobus";
+
+       snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
+                dev->udev->bus->busnum, dev->udev->devnum);
+
+       dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!dev->mdiobus->irq) {
+               ret = -ENOMEM;
+               goto exit1;
+       }
+
+       /* handle our own interrupt */
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT;
+
+       switch (dev->devid & ID_REV_CHIP_ID_MASK_) {
+       case 0x78000000:
+       case 0x78500000:
+               /* set to internal PHY id */
+               dev->mdiobus->phy_mask = ~(1 << 1);
+               break;
+       }
+
+       ret = mdiobus_register(dev->mdiobus);
+       if (ret) {
+               netdev_err(dev->net, "can't register MDIO bus\n");
+               goto exit2;
+       }
+
+       netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id);
+       return 0;
+exit2:
+       kfree(dev->mdiobus->irq);
+exit1:
+       mdiobus_free(dev->mdiobus);
+       return ret;
+}
+
+static void lan78xx_remove_mdio(struct lan78xx_net *dev)
+{
+       mdiobus_unregister(dev->mdiobus);
+       kfree(dev->mdiobus->irq);
+       mdiobus_free(dev->mdiobus);
+}
+
+static void lan78xx_link_status_change(struct net_device *net)
+{
+       /* nothing to do */
 }
 
 static int lan78xx_phy_init(struct lan78xx_net *dev)
 {
-       int temp;
-       struct mii_if_info *mii = &dev->mii;
+       int ret;
+       struct phy_device *phydev = dev->net->phydev;
 
-       if ((!mii->mdio_write) || (!mii->mdio_read))
-               return -EOPNOTSUPP;
+       phydev = phy_find_first(dev->mdiobus);
+       if (!phydev) {
+               netdev_err(dev->net, "no PHY found\n");
+               return -EIO;
+       }
 
-       temp = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
-       temp |= ADVERTISE_ALL;
-       mii->mdio_write(mii->dev, mii->phy_id, MII_ADVERTISE,
-                       temp | ADVERTISE_CSMA |
-                       ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+       ret = phy_connect_direct(dev->net, phydev,
+                                lan78xx_link_status_change,
+                                PHY_INTERFACE_MODE_GMII);
+       if (ret) {
+               netdev_err(dev->net, "can't attach PHY to %s\n",
+                          dev->mdiobus->id);
+               return -EIO;
+       }
 
        /* set to AUTOMDIX */
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
-       temp = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL);
-       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-       mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL,
-                       temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0);
-       dev->mdix_ctrl = ETH_TP_MDI_AUTO;
-
-       /* MAC doesn't support 1000HD */
-       temp = mii->mdio_read(mii->dev, mii->phy_id, MII_CTRL1000);
-       mii->mdio_write(mii->dev, mii->phy_id, MII_CTRL1000,
-                       temp & ~ADVERTISE_1000HALF);
-
-       /* clear interrupt */
-       mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS);
-       mii->mdio_write(mii->dev, mii->phy_id, PHY_VTSE_INT_MASK,
-                       PHY_VTSE_INT_MASK_MDINTPIN_EN_ |
-                       PHY_VTSE_INT_MASK_LINK_CHANGE_);
+       lan78xx_set_mdix_status(dev->net, ETH_TP_MDI_AUTO);
+
+       /* MAC doesn't support 1000T Half */
+       phydev->supported &= ~SUPPORTED_1000baseT_Half;
+       phydev->supported |= (SUPPORTED_10baseT_Half |
+                             SUPPORTED_10baseT_Full |
+                             SUPPORTED_100baseT_Half |
+                             SUPPORTED_100baseT_Full |
+                             SUPPORTED_1000baseT_Full |
+                             SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+       genphy_config_aneg(phydev);
+
+       /* Workaround to enable PHY interrupt.
+        * phy_start_interrupts() is API for requesting and enabling
+        * PHY interrupt. However, USB-to-Ethernet device can't use
+        * request_irq() called in phy_start_interrupts().
+        * Set PHY to PHY_HALTED and call phy_start()
+        * to make a call to phy_enable_interrupts()
+        */
+       phy_stop(phydev);
+       phy_start(phydev);
 
        netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
 
@@ -1930,6 +1830,10 @@ static int lan78xx_reset(struct lan78xx_net *dev)
 
        lan78xx_init_mac_address(dev);
 
+       /* save DEVID for later usage */
+       ret = lan78xx_read_reg(dev, ID_REV, &buf);
+       dev->devid = buf;
+
        /* Respond to the IN token with a NAK */
        ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
        buf |= USB_CFG_BIR_;
@@ -2002,23 +1906,12 @@ static int lan78xx_reset(struct lan78xx_net *dev)
                        netdev_warn(dev->net, "timeout waiting for PHY Reset");
                        return -EIO;
                }
-       } while (buf & PMT_CTL_PHY_RST_);
-
-       lan78xx_mii_init(dev);
-
-       ret = lan78xx_phy_init(dev);
+       } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_));
 
        ret = lan78xx_read_reg(dev, MAC_CR, &buf);
-
-       buf |= MAC_CR_GMII_EN_;
        buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
-
        ret = lan78xx_write_reg(dev, MAC_CR, buf);
 
-       /* enable on PHY */
-       if (buf & MAC_CR_EEE_EN_)
-               lan78xx_mmd_write(dev->net, dev->mii.phy_id, 0x07, 0x3C, 0x06);
-
        /* enable PHY interrupts */
        ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf);
        buf |= INT_ENP_PHY_INT;
@@ -2042,9 +1935,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
        buf |= FCT_RX_CTL_EN_;
        ret = lan78xx_write_reg(dev, FCT_RX_CTL, buf);
 
-       if (!mii_nway_restart(&dev->mii))
-               netif_dbg(dev, link, dev->net, "autoneg initiated");
-
        return 0;
 }
 
@@ -2061,6 +1951,10 @@ static int lan78xx_open(struct net_device *net)
        if (ret < 0)
                goto done;
 
+       ret = lan78xx_phy_init(dev);
+       if (ret < 0)
+               goto done;
+
        /* for Link Check */
        if (dev->urb_intr) {
                ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
@@ -2115,6 +2009,10 @@ int lan78xx_stop(struct net_device *net)
 {
        struct lan78xx_net              *dev = netdev_priv(net);
 
+       phy_stop(net->phydev);
+       phy_disconnect(net->phydev);
+       net->phydev = NULL;
+
        clear_bit(EVENT_DEV_OPEN, &dev->flags);
        netif_stop_queue(net);
 
@@ -2395,6 +2293,8 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf)
        /* Init all registers */
        ret = lan78xx_reset(dev);
 
+       lan78xx_mdio_init(dev);
+
        dev->net->flags |= IFF_MULTICAST;
 
        pdata->wol = WAKE_MAGIC;
@@ -2406,6 +2306,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf)
 {
        struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
 
+       lan78xx_remove_mdio(dev);
+
        if (pdata) {
                netif_dbg(dev, ifdown, dev->net, "free pdata");
                kfree(pdata);
@@ -2522,11 +2424,6 @@ static int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb)
                        skb_pull(skb, align_count);
        }
 
-       if (unlikely(skb->len < 0)) {
-               netdev_warn(dev->net, "invalid rx length<0 %d", skb->len);
-               return 0;
-       }
-
        return 1;
 }
 
@@ -3307,7 +3204,6 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
        int ret;
        int event;
 
-       ret = 0;
        event = message.event;
 
        if (!dev->suspend_count++) {
@@ -3389,6 +3285,7 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
                }
        }
 
+       ret = 0;
 out:
        return ret;
 }
@@ -3459,6 +3356,9 @@ int lan78xx_reset_resume(struct usb_interface *intf)
        struct lan78xx_net *dev = usb_get_intfdata(intf);
 
        lan78xx_reset(dev);
+
+       lan78xx_phy_init(dev);
+
        return lan78xx_resume(intf);
 }