diff --git a/MAINTAINERS b/MAINTAINERS index 27393cff1707b..12b764f4c93c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11332,6 +11332,13 @@ S: Maintained F: drivers/usb/host/isp116x* F: include/linux/usb/isp116x.h +USB LAN78XX ETHERNET DRIVER +M: Woojung Huh +M: Microchip Linux Driver Support +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/usb/lan78xx.* + USB MASS STORAGE DRIVER M: Matthew Dharm L: linux-usb@vger.kernel.org diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 1c299b8a162d7..705c180163c5c 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -36,7 +36,7 @@ #define DRIVER_AUTHOR "WOOJUNG HUH " #define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices" #define DRIVER_NAME "lan78xx" -#define DRIVER_VERSION "1.0.2" +#define DRIVER_VERSION "1.0.3" #define TX_TIMEOUT_JIFFIES (5 * HZ) #define THROTTLE_JIFFIES (HZ / 8) @@ -278,8 +278,12 @@ struct lan78xx_net { int link_on; u8 mdix_ctrl; - u32 devid; + u32 chipid; + u32 chiprev; struct mii_bus *mdiobus; + + int fc_autoneg; + u8 fc_request_control; }; /* use ethtool to change the level for any given device */ @@ -471,7 +475,7 @@ static int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset, */ ret = lan78xx_read_reg(dev, HW_CFG, &val); saved = val; - if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) { + if (dev->chipid == ID_REV_CHIP_ID_7800_) { val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_); ret = lan78xx_write_reg(dev, HW_CFG, val); } @@ -505,7 +509,7 @@ static int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset, retval = 0; exit: - if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) + if (dev->chipid == ID_REV_CHIP_ID_7800_) ret = lan78xx_write_reg(dev, HW_CFG, saved); return retval; @@ -539,7 +543,7 @@ static int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset, */ ret = lan78xx_read_reg(dev, HW_CFG, &val); saved = val; - if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) { + if (dev->chipid == ID_REV_CHIP_ID_7800_) { val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_); ret = lan78xx_write_reg(dev, HW_CFG, val); } @@ -587,7 +591,7 @@ static int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset, retval = 0; exit: - if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) + if (dev->chipid == ID_REV_CHIP_ID_7800_) ret = lan78xx_write_reg(dev, HW_CFG, saved); return retval; @@ -901,11 +905,15 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, { u32 flow = 0, fct_flow = 0; int ret; + u8 cap; - u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + if (dev->fc_autoneg) + cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + else + cap = dev->fc_request_control; if (cap & FLOW_CTRL_TX) - flow = (FLOW_CR_TX_FCEN_ | 0xFFFF); + flow |= (FLOW_CR_TX_FCEN_ | 0xFFFF); if (cap & FLOW_CTRL_RX) flow |= FLOW_CR_RX_FCEN_; @@ -1385,6 +1393,62 @@ static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd) return ret; } +static void lan78xx_get_pause(struct net_device *net, + struct ethtool_pauseparam *pause) +{ + struct lan78xx_net *dev = netdev_priv(net); + struct phy_device *phydev = net->phydev; + struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + + phy_ethtool_gset(phydev, &ecmd); + + pause->autoneg = dev->fc_autoneg; + + if (dev->fc_request_control & FLOW_CTRL_TX) + pause->tx_pause = 1; + + if (dev->fc_request_control & FLOW_CTRL_RX) + pause->rx_pause = 1; +} + +static int lan78xx_set_pause(struct net_device *net, + struct ethtool_pauseparam *pause) +{ + struct lan78xx_net *dev = netdev_priv(net); + struct phy_device *phydev = net->phydev; + struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + int ret; + + phy_ethtool_gset(phydev, &ecmd); + + if (pause->autoneg && !ecmd.autoneg) { + ret = -EINVAL; + goto exit; + } + + dev->fc_request_control = 0; + if (pause->rx_pause) + dev->fc_request_control |= FLOW_CTRL_RX; + + if (pause->tx_pause) + dev->fc_request_control |= FLOW_CTRL_TX; + + if (ecmd.autoneg) { + u32 mii_adv; + + ecmd.advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); + ecmd.advertising |= mii_adv_to_ethtool_adv_t(mii_adv); + phy_ethtool_sset(phydev, &ecmd); + } + + dev->fc_autoneg = pause->autoneg; + + ret = 0; +exit: + return ret; +} + static const struct ethtool_ops lan78xx_ethtool_ops = { .get_link = lan78xx_get_link, .nway_reset = lan78xx_nway_reset, @@ -1403,6 +1467,8 @@ static const struct ethtool_ops lan78xx_ethtool_ops = { .set_wol = lan78xx_set_wol, .get_eee = lan78xx_get_eee, .set_eee = lan78xx_set_eee, + .get_pauseparam = lan78xx_get_pause, + .set_pauseparam = lan78xx_set_pause, }; static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) @@ -1555,9 +1621,9 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev) snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); - switch (dev->devid & ID_REV_CHIP_ID_MASK_) { - case 0x78000000: - case 0x78500000: + switch (dev->chipid) { + case ID_REV_CHIP_ID_7800_: + case ID_REV_CHIP_ID_7850_: /* set to internal PHY id */ dev->mdiobus->phy_mask = ~(1 << 1); break; @@ -1590,6 +1656,7 @@ static void lan78xx_link_status_change(struct net_device *net) static int lan78xx_phy_init(struct lan78xx_net *dev) { int ret; + u32 mii_adv; struct phy_device *phydev = dev->net->phydev; phydev = phy_find_first(dev->mdiobus); @@ -1622,14 +1689,17 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) /* 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); + + /* support both flow controls */ + dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX); + phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); + phydev->advertising |= mii_adv_to_ethtool_adv_t(mii_adv); + genphy_config_aneg(phydev); + dev->fc_autoneg = phydev->autoneg; + phy_start(phydev); netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); @@ -1918,7 +1988,8 @@ static int lan78xx_reset(struct lan78xx_net *dev) /* save DEVID for later usage */ ret = lan78xx_read_reg(dev, ID_REV, &buf); - dev->devid = buf; + dev->chipid = (buf & ID_REV_CHIP_ID_MASK_) >> 16; + dev->chiprev = buf & ID_REV_CHIP_REV_MASK_; /* Respond to the IN token with a NAK */ ret = lan78xx_read_reg(dev, USB_CFG0, &buf); diff --git a/drivers/net/usb/lan78xx.h b/drivers/net/usb/lan78xx.h index a93fb653e7c52..40927906109aa 100644 --- a/drivers/net/usb/lan78xx.h +++ b/drivers/net/usb/lan78xx.h @@ -107,6 +107,7 @@ #define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) #define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) #define ID_REV_CHIP_ID_7800_ (0x7800) +#define ID_REV_CHIP_ID_7850_ (0x7850) #define FPGA_REV (0x04) #define FPGA_REV_MINOR_MASK_ (0x0000FF00)