1
0
Fork 0

lan78xx: Update to use phylib instead of mii_if_info.

Update to use phylib instead of mii_if_info.

Signed-off-by: Woojung Huh <woojung.huh@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
hifive-unleashed-5.1
Woojung.Huh@microchip.com 2015-09-16 23:40:54 +00:00 committed by David S. Miller
parent 05fe68c008
commit ce85e13ad6
1 changed files with 255 additions and 361 deletions

View 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>
@ -36,7 +35,7 @@
#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 +56,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 +273,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 +411,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 +831,14 @@ 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, PHY_VTSE_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, PHY_VTSE_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,
@ -1405,24 +1178,19 @@ static void lan78xx_set_msglevel(struct net_device *net, u32 level)
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);
phy_write(phydev, PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
buf = phy_read(phydev, PHY_EXT_MODE_CTRL);
phy_write(phydev, PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0);
buf &= PHY_EXT_MODE_CTRL_MDIX_MASK_;
if (buf == PHY_EXT_MODE_CTRL_AUTO_MDIX_) {
@ -1444,70 +1212,54 @@ 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);
phy_write(phydev, PHY_EXT_GPIO_PAGE,
PHY_EXT_GPIO_PAGE_SPACE_1);
temp = phy_read(phydev, 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);
phy_write(phydev, PHY_EXT_MODE_CTRL,
temp | PHY_EXT_MODE_CTRL_MDI_);
phy_write(phydev, 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);
phy_write(phydev, PHY_EXT_GPIO_PAGE,
PHY_EXT_GPIO_PAGE_SPACE_1);
temp = phy_read(phydev, 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);
phy_write(phydev, PHY_EXT_MODE_CTRL,
temp | PHY_EXT_MODE_CTRL_MDI_X_);
phy_write(phydev, 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);
phy_write(phydev, PHY_EXT_GPIO_PAGE,
PHY_EXT_GPIO_PAGE_SPACE_1);
temp = phy_read(phydev, 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);
phy_write(phydev, PHY_EXT_MODE_CTRL,
temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
phy_write(phydev, PHY_EXT_GPIO_PAGE,
PHY_EXT_GPIO_PAGE_SPACE_0);
}
}
/* 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 +1289,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 +1348,189 @@ 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);
phy_write(phydev, PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
ret = phy_read(phydev, PHY_EXT_MODE_CTRL);
ret &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
phy_write(phydev, PHY_EXT_MODE_CTRL,
ret | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
phy_write(phydev, 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);
/* 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);
/* 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_);
/* 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 +1816,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_;
@ -2004,10 +1894,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
}
} while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_));
lan78xx_mii_init(dev);
ret = lan78xx_phy_init(dev);
ret = lan78xx_read_reg(dev, MAC_CR, &buf);
buf |= MAC_CR_GMII_EN_;
@ -2015,10 +1901,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
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 +1924,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 +1940,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 +1998,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 +2282,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 +2295,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);
@ -3459,6 +3350,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);
}