Loading drivers/net/usb/lan78xx.c +256 −362 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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) Loading @@ -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 Loading Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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) { Loading @@ -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; Loading Loading @@ -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; Loading @@ -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) Loading @@ -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_; Loading @@ -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, Loading Loading @@ -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_) { Loading @@ -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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_MDI_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_MDI_X_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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); Loading Loading @@ -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) Loading Loading @@ -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); /* 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_); /* 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"); Loading Loading @@ -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_; Loading Loading @@ -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_; Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); } Loading Loading
drivers/net/usb/lan78xx.c +256 −362 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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) Loading @@ -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 Loading Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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) { Loading @@ -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; Loading Loading @@ -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; Loading @@ -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) Loading @@ -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_; Loading @@ -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, Loading Loading @@ -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_) { Loading @@ -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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_MDI_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_MDI_X_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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_write(phydev, 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_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, phy_write(phydev, PHY_EXT_MODE_CTRL, temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_); mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_GPIO_PAGE, 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); Loading Loading @@ -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) Loading Loading @@ -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); /* 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_); /* 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"); Loading Loading @@ -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_; Loading Loading @@ -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_; Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); } Loading