diff options
Diffstat (limited to 'drivers/net/usb/ax88179_178a.c')
| -rw-r--r-- | drivers/net/usb/ax88179_178a.c | 602 |
1 files changed, 317 insertions, 285 deletions
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 1a627ba4b850..b034ef8a73ea 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -164,11 +164,16 @@ #define GMII_PHY_PGSEL_PAGE3 0x0003 #define GMII_PHY_PGSEL_PAGE5 0x0005 +static int ax88179_reset(struct usbnet *dev); + struct ax88179_data { u8 eee_enabled; u8 eee_active; u16 rxctl; - u16 reserved; + u8 in_pm; + u32 wol_supported; + u32 wolopts; + u8 disconnecting; }; struct ax88179_int_data { @@ -185,15 +190,30 @@ static const struct { {7, 0xcc, 0x4c, 0x18, 8}, }; +static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode) +{ + struct ax88179_data *ax179_data = dev->driver_priv; + + ax179_data->in_pm = pm_mode; +} + +static int ax88179_in_pm(struct usbnet *dev) +{ + struct ax88179_data *ax179_data = dev->driver_priv; + + return ax179_data->in_pm; +} + static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data, int in_pm) + u16 size, void *data) { int ret; int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); + struct ax88179_data *ax179_data = dev->driver_priv; BUG_ON(!dev); - if (!in_pm) + if (!ax88179_in_pm(dev)) fn = usbnet_read_cmd; else fn = usbnet_read_cmd_nopm; @@ -201,7 +221,7 @@ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, size); - if (unlikely(ret < 0)) + if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting))) netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", index, ret); @@ -209,14 +229,15 @@ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, } static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, const void *data, int in_pm) + u16 size, const void *data) { int ret; int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); + struct ax88179_data *ax179_data = dev->driver_priv; BUG_ON(!dev); - if (!in_pm) + if (!ax88179_in_pm(dev)) fn = usbnet_write_cmd; else fn = usbnet_write_cmd_nopm; @@ -224,7 +245,7 @@ static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, size); - if (unlikely(ret < 0)) + if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting))) netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", index, ret); @@ -249,47 +270,6 @@ static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, } } -static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, - u16 index, u16 size, void *data) -{ - int ret; - - if (2 == size) { - u16 buf; - ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); - le16_to_cpus(&buf); - *((u16 *)data) = buf; - } else if (4 == size) { - u32 buf; - ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); - le32_to_cpus(&buf); - *((u32 *)data) = buf; - } else { - ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 1); - } - - return ret; -} - -static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, - u16 index, u16 size, const void *data) -{ - int ret; - - if (2 == size) { - u16 buf; - buf = *((u16 *)data); - cpu_to_le16s(&buf); - ret = __ax88179_write_cmd(dev, cmd, value, index, - size, &buf, 1); - } else { - ret = __ax88179_write_cmd(dev, cmd, value, index, - size, data, 1); - } - - return ret; -} - static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data) { @@ -297,16 +277,16 @@ static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, if (2 == size) { u16 buf = 0; - ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); + ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf); le16_to_cpus(&buf); *((u16 *)data) = buf; } else if (4 == size) { u32 buf = 0; - ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); + ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf); le32_to_cpus(&buf); *((u32 *)data) = buf; } else { - ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 0); + ret = __ax88179_read_cmd(dev, cmd, value, index, size, data); } return ret; @@ -322,10 +302,10 @@ static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, buf = *((u16 *)data); cpu_to_le16s(&buf); ret = __ax88179_write_cmd(dev, cmd, value, index, - size, &buf, 0); + size, &buf); } else { ret = __ax88179_write_cmd(dev, cmd, value, index, - size, data, 0); + size, data); } return ret; @@ -346,7 +326,8 @@ static void ax88179_status(struct usbnet *dev, struct urb *urb) if (netif_carrier_ok(dev->net) != link) { usbnet_link_change(dev, link, 1); - netdev_info(dev->net, "ax88179 - Link status is: %d\n", link); + if (!link) + netdev_info(dev->net, "ax88179 - Link status is: 0\n"); } } @@ -425,55 +406,63 @@ ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad, static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *priv = dev->driver_priv; u16 tmp16; u8 tmp8; + ax88179_set_pm_mode(dev, true); + usbnet_suspend(intf, message); + /* Enable WoL */ + if (priv->wolopts) { + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, + 1, 1, &tmp8); + if (priv->wolopts & WAKE_PHY) + tmp8 |= AX_MONITOR_MODE_RWLC; + if (priv->wolopts & WAKE_MAGIC) + tmp8 |= AX_MONITOR_MODE_RWMP; + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, + 1, 1, &tmp8); + } + /* Disable RX path */ - ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); tmp16 &= ~AX_MEDIUM_RECEIVE_EN; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, &tmp16); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); /* Force bulk-in zero length */ - ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, - 2, 2, &tmp16); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, + 2, 2, &tmp16); tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, - 2, 2, &tmp16); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, + 2, 2, &tmp16); /* change clock */ tmp8 = 0; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); /* Configure RX control register => stop operation */ tmp16 = AX_RX_CTL_STOP; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + + ax88179_set_pm_mode(dev, false); return 0; } /* This function is used to enable the autodetach function. */ /* This function is determined by offset 0x43 of EEPROM */ -static int ax88179_auto_detach(struct usbnet *dev, int in_pm) +static int ax88179_auto_detach(struct usbnet *dev) { u16 tmp16; u8 tmp8; - int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *); - int (*fnw)(struct usbnet *, u8, u16, u16, u16, const void *); - - if (!in_pm) { - fnr = ax88179_read_cmd; - fnw = ax88179_write_cmd; - } else { - fnr = ax88179_read_cmd_nopm; - fnw = ax88179_write_cmd_nopm; - } - if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0) + if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0) return 0; if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100))) @@ -481,13 +470,13 @@ static int ax88179_auto_detach(struct usbnet *dev, int in_pm) /* Enable Auto Detach bit */ tmp8 = 0; - fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); tmp8 |= AX_CLK_SELECT_ULR; - fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); - fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); tmp16 |= AX_PHYPWR_RSTCTL_AT; - fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); return 0; } @@ -495,77 +484,52 @@ static int ax88179_auto_detach(struct usbnet *dev, int in_pm) static int ax88179_resume(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - u16 tmp16; - u8 tmp8; + + ax88179_set_pm_mode(dev, true); usbnet_link_change(dev, 0, 0); - /* Power up ethernet PHY */ - tmp16 = 0; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, - 2, 2, &tmp16); - udelay(1000); + ax88179_reset(dev); - tmp16 = AX_PHYPWR_RSTCTL_IPRL; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, - 2, 2, &tmp16); - msleep(200); + ax88179_set_pm_mode(dev, false); - /* Ethernet PHY Auto Detach*/ - ax88179_auto_detach(dev, 1); + return usbnet_resume(intf); +} - /* Enable clock */ - ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); - tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); - msleep(100); +static void ax88179_disconnect(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *ax179_data; - /* Configure RX control register => start operation */ - tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; - ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + if (!dev) + return; - return usbnet_resume(intf); + ax179_data = dev->driver_priv; + ax179_data->disconnecting = 1; + + usbnet_disconnect(intf); } static void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = netdev_priv(net); - u8 opt; + struct ax88179_data *priv = dev->driver_priv; - if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, - 1, 1, &opt) < 0) { - wolinfo->supported = 0; - wolinfo->wolopts = 0; - return; - } - - wolinfo->supported = WAKE_PHY | WAKE_MAGIC; - wolinfo->wolopts = 0; - if (opt & AX_MONITOR_MODE_RWLC) - wolinfo->wolopts |= WAKE_PHY; - if (opt & AX_MONITOR_MODE_RWMP) - wolinfo->wolopts |= WAKE_MAGIC; + wolinfo->supported = priv->wol_supported; + wolinfo->wolopts = priv->wolopts; } static int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = netdev_priv(net); - u8 opt = 0; + struct ax88179_data *priv = dev->driver_priv; - if (wolinfo->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) + if (wolinfo->wolopts & ~(priv->wol_supported)) return -EINVAL; - if (wolinfo->wolopts & WAKE_PHY) - opt |= AX_MONITOR_MODE_RWLC; - if (wolinfo->wolopts & WAKE_MAGIC) - opt |= AX_MONITOR_MODE_RWMP; - - if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, - 1, 1, &opt) < 0) - return -EINVAL; + priv->wolopts = wolinfo->wolopts; return 0; } @@ -599,8 +563,7 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, /* ax88179/178A returns 2 bytes from eeprom on read */ for (i = first_word; i <= last_word; i++) { ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2, - &eeprom_buff[i - first_word], - 0); + &eeprom_buff[i - first_word]); if (ret < 0) { kfree(eeprom_buff); return -EIO; @@ -705,7 +668,7 @@ static int ax88179_set_link_ksettings(struct net_device *net, } static int -ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) +ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_keee *data) { int val; @@ -714,29 +677,29 @@ ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) MDIO_MMD_PCS); if (val < 0) return val; - data->supported = mmd_eee_cap_to_ethtool_sup_t(val); + mii_eee_cap1_mod_linkmode_t(data->supported, val); /* Get advertisement EEE */ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); if (val < 0) return val; - data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); + mii_eee_cap1_mod_linkmode_t(data->advertised, val); /* Get LP advertisement EEE */ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); if (val < 0) return val; - data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); + mii_eee_cap1_mod_linkmode_t(data->lp_advertised, val); return 0; } static int -ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) +ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_keee *data) { - u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + u16 tmp16 = linkmode_to_mii_eee_cap1_t(data->advertised); return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, tmp16); @@ -745,7 +708,7 @@ ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) static int ax88179_chk_eee(struct usbnet *dev) { struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; - struct ax88179_data *priv = (struct ax88179_data *)dev->data; + struct ax88179_data *priv = dev->driver_priv; mii_ethtool_gset(&dev->mii, &ecmd); @@ -845,10 +808,10 @@ static void ax88179_enable_eee(struct usbnet *dev) GMII_PHY_PAGE_SELECT, 2, &tmp16); } -static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) +static int ax88179_get_eee(struct net_device *net, struct ethtool_keee *edata) { struct usbnet *dev = netdev_priv(net); - struct ax88179_data *priv = (struct ax88179_data *)dev->data; + struct ax88179_data *priv = dev->driver_priv; edata->eee_enabled = priv->eee_enabled; edata->eee_active = priv->eee_active; @@ -856,10 +819,10 @@ static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) return ax88179_ethtool_get_eee(dev, edata); } -static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata) +static int ax88179_set_eee(struct net_device *net, struct ethtool_keee *edata) { struct usbnet *dev = netdev_priv(net); - struct ax88179_data *priv = (struct ax88179_data *)dev->data; + struct ax88179_data *priv = dev->driver_priv; int ret; priv->eee_enabled = edata->eee_enabled; @@ -910,8 +873,8 @@ static const struct ethtool_ops ax88179_ethtool_ops = { static void ax88179_set_multicast(struct net_device *net) { struct usbnet *dev = netdev_priv(net); - struct ax88179_data *data = (struct ax88179_data *)dev->data; - u8 *m_filter = ((u8 *)dev->data) + 12; + struct ax88179_data *data = dev->driver_priv; + u8 *m_filter = ((u8 *)dev->data); data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE); @@ -923,7 +886,7 @@ static void ax88179_set_multicast(struct net_device *net) } else if (netdev_mc_empty(net)) { /* just broadcast and directed */ } else { - /* We use the 20 byte dev->data for our 8 byte filter buffer + /* We use dev->data for our 8 byte filter buffer * to avoid allocating memory that is tricky to free later */ u32 crc_bits; @@ -981,7 +944,7 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) struct usbnet *dev = netdev_priv(net); u16 tmp16; - net->mtu = new_mtu; + WRITE_ONCE(net->mtu, new_mtu); dev->hard_mtu = net->mtu + net->hard_header_len; if (net->mtu > 1500) { @@ -1069,7 +1032,7 @@ static int ax88179_check_eeprom(struct usbnet *dev) } while (buf & EEP_BUSY); __ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, - 2, 2, &eeprom[i * 2], 0); + 2, 2, &eeprom[i * 2]); if ((i == 0) && (eeprom[0] == 0xFF)) return -EINVAL; @@ -1311,9 +1274,10 @@ static void ax88179_get_mac_addr(struct usbnet *dev) if (is_valid_ether_addr(mac)) { eth_hw_addr_set(dev->net, mac); + if (!is_local_ether_addr(mac)) + dev->net->addr_assign_type = NET_ADDR_PERM; } else { netdev_info(dev->net, "invalid MAC address, using random\n"); - eth_hw_addr_random(dev->net); } ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, @@ -1322,46 +1286,18 @@ static void ax88179_get_mac_addr(struct usbnet *dev) static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) { - u8 buf[5]; - u16 *tmp16; - u8 *tmp; - struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; - struct ethtool_eee eee_data; - - usbnet_get_endpoints(dev, intf); - - tmp16 = (u16 *)buf; - tmp = (u8 *)buf; - - memset(ax179_data, 0, sizeof(*ax179_data)); - - /* Power up ethernet PHY */ - *tmp16 = 0; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); - *tmp16 = AX_PHYPWR_RSTCTL_IPRL; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); - msleep(200); - - *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); - msleep(100); - - /* Read MAC address from DTB or asix chip */ - ax88179_get_mac_addr(dev); - memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); - - /* RX bulk configuration */ - memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + struct ax88179_data *ax179_data; + int ret; - dev->rx_urb_size = 1024 * 20; + ret = usbnet_get_endpoints(dev, intf); + if (ret < 0) + return ret; - *tmp = 0x34; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); + ax179_data = kzalloc(sizeof(*ax179_data), GFP_KERNEL); + if (!ax179_data) + return -ENOMEM; - *tmp = 0x52; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, - 1, 1, tmp); + dev->driver_priv = ax179_data; dev->net->netdev_ops = &ax88179_netdev_ops; dev->net->ethtool_ops = &ax88179_ethtool_ops; @@ -1382,54 +1318,16 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->hw_features |= dev->net->features; - netif_set_gso_max_size(dev->net, 16384); - - /* Enable checksum offload */ - *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | - AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); - - *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | - AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); - - /* Configure RX control register => start operation */ - *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); - - *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | - AX_MONITOR_MODE_RWMP; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); - - /* Configure default medium type => giga */ - *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | - AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | - AX_MEDIUM_GIGAMODE; - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, - 2, 2, tmp16); - - ax88179_led_setting(dev); - - ax179_data->eee_enabled = 0; - ax179_data->eee_active = 0; - - ax88179_disable_eee(dev); + netif_set_tso_max_size(dev->net, 16384); - ax88179_ethtool_get_eee(dev, &eee_data); - eee_data.advertised = 0; - ax88179_ethtool_set_eee(dev, &eee_data); - - /* Restart autoneg */ - mii_nway_restart(&dev->mii); - - usbnet_link_change(dev, 0, 0); + ax88179_reset(dev); return 0; } static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf) { + struct ax88179_data *ax179_data = dev->driver_priv; u16 tmp16; /* Configure RX control register => stop operation */ @@ -1442,6 +1340,8 @@ static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf) /* Power down ethernet PHY */ tmp16 = 0; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + + kfree(ax179_data); } static void @@ -1468,58 +1368,114 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) u16 hdr_off; u32 *pkt_hdr; - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) + /* At the end of the SKB, there's a header telling us how many packets + * are bundled into this buffer and where we can find an array of + * per-packet metadata (which contains elements encoded into u16). + */ + + /* SKB contents for current firmware: + * <packet 1> <padding> + * ... + * <packet N> <padding> + * <per-packet metadata entry 1> <dummy header> + * ... + * <per-packet metadata entry N> <dummy header> + * <padding2> <rx_hdr> + * + * where: + * <packet N> contains pkt_len bytes: + * 2 bytes of IP alignment pseudo header + * packet received + * <per-packet metadata entry N> contains 4 bytes: + * pkt_len and fields AX_RXHDR_* + * <padding> 0-7 bytes to terminate at + * 8 bytes boundary (64-bit). + * <padding2> 4 bytes to make rx_hdr terminate at + * 8 bytes boundary (64-bit) + * <dummy-header> contains 4 bytes: + * pkt_len=0 and AX_RXHDR_DROP_ERR + * <rx-hdr> contains 4 bytes: + * pkt_cnt and hdr_off (offset of + * <per-packet metadata entry 1>) + * + * pkt_cnt is number of entrys in the per-packet metadata. + * In current firmware there is 2 entrys per packet. + * The first points to the packet and the + * second is a dummy header. + * This was done probably to align fields in 64-bit and + * maintain compatibility with old firmware. + * This code assumes that <dummy header> and <padding2> are + * optional. + */ + + if (skb->len < 4) return 0; - skb_trim(skb, skb->len - 4); rx_hdr = get_unaligned_le32(skb_tail_pointer(skb)); - pkt_cnt = (u16)rx_hdr; hdr_off = (u16)(rx_hdr >> 16); + + if (pkt_cnt == 0) + return 0; + + /* Make sure that the bounds of the metadata array are inside the SKB + * (and in front of the counter at the end). + */ + if (pkt_cnt * 4 + hdr_off > skb->len) + return 0; pkt_hdr = (u32 *)(skb->data + hdr_off); - while (pkt_cnt--) { + /* Packets must not overlap the metadata array */ + skb_trim(skb, hdr_off); + + for (; pkt_cnt > 0; pkt_cnt--, pkt_hdr++) { + u16 pkt_len_plus_padd; u16 pkt_len; le32_to_cpus(pkt_hdr); pkt_len = (*pkt_hdr >> 16) & 0x1fff; + pkt_len_plus_padd = (pkt_len + 7) & 0xfff8; + + /* Skip dummy header used for alignment + */ + if (pkt_len == 0) + continue; + + if (pkt_len_plus_padd > skb->len) + return 0; /* Check CRC or runt packet */ - if ((*pkt_hdr & AX_RXHDR_CRC_ERR) || - (*pkt_hdr & AX_RXHDR_DROP_ERR)) { - skb_pull(skb, (pkt_len + 7) & 0xFFF8); - pkt_hdr++; + if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) || + pkt_len < 2 + ETH_HLEN) { + dev->net->stats.rx_errors++; + skb_pull(skb, pkt_len_plus_padd); continue; } - if (pkt_cnt == 0) { - skb->len = pkt_len; + /* last packet */ + if (pkt_len_plus_padd == skb->len) { + skb_trim(skb, pkt_len); + /* Skip IP alignment pseudo header */ skb_pull(skb, 2); - skb_set_tail_pointer(skb, skb->len); - skb->truesize = pkt_len + sizeof(struct sk_buff); + ax88179_rx_checksum(skb, pkt_hdr); return 1; } - ax_skb = skb_clone(skb, GFP_ATOMIC); - if (ax_skb) { - ax_skb->len = pkt_len; - /* Skip IP alignment pseudo header */ - skb_pull(ax_skb, 2); - skb_set_tail_pointer(ax_skb, ax_skb->len); - ax_skb->truesize = pkt_len + sizeof(struct sk_buff); - ax88179_rx_checksum(ax_skb, pkt_hdr); - usbnet_skb_return(dev, ax_skb); - } else { + ax_skb = netdev_alloc_skb_ip_align(dev->net, pkt_len); + if (!ax_skb) return 0; - } + skb_put(ax_skb, pkt_len); + memcpy(ax_skb->data, skb->data + 2, pkt_len); + + ax88179_rx_checksum(ax_skb, pkt_hdr); + usbnet_skb_return(dev, ax_skb); - skb_pull(skb, (pkt_len + 7) & 0xFFF8); - pkt_hdr++; + skb_pull(skb, pkt_len_plus_padd); } - return 1; + + return 0; } static struct sk_buff * @@ -1557,7 +1513,7 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) static int ax88179_link_reset(struct usbnet *dev) { - struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; + struct ax88179_data *ax179_data = dev->driver_priv; u8 tmp[5], link_sts; u16 mode, tmp16, delay = HZ / 10; u32 tmp32 = 0x40000000; @@ -1587,6 +1543,7 @@ static int ax88179_link_reset(struct usbnet *dev) GMII_PHY_PHYSR, 2, &tmp16); if (!(tmp16 & GMII_PHY_PHYSR_LINK)) { + netdev_info(dev->net, "ax88179 - Link status is: 0\n"); return 0; } else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) { mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ; @@ -1624,6 +1581,8 @@ static int ax88179_link_reset(struct usbnet *dev) netif_carrier_on(dev->net); + netdev_info(dev->net, "ax88179 - Link status is: 1\n"); + return 0; } @@ -1632,8 +1591,8 @@ static int ax88179_reset(struct usbnet *dev) u8 buf[5]; u16 *tmp16; u8 *tmp; - struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; - struct ethtool_eee eee_data; + struct ax88179_data *ax179_data = dev->driver_priv; + struct ethtool_keee eee_data; tmp16 = (u16 *)buf; tmp = (u8 *)buf; @@ -1644,17 +1603,18 @@ static int ax88179_reset(struct usbnet *dev) *tmp16 = AX_PHYPWR_RSTCTL_IPRL; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); - msleep(200); + msleep(500); *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); - msleep(100); + msleep(200); /* Ethernet PHY Auto Detach*/ - ax88179_auto_detach(dev, 0); + ax88179_auto_detach(dev); /* Read MAC address from DTB or asix chip */ ax88179_get_mac_addr(dev); + memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); /* RX bulk configuration */ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); @@ -1669,12 +1629,6 @@ static int ax88179_reset(struct usbnet *dev) ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); - dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; - - dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM; - /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; @@ -1700,6 +1654,12 @@ static int ax88179_reset(struct usbnet *dev) ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); + /* Check if WoL is supported */ + ax179_data->wol_supported = 0; + if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, + 1, 1, &tmp) > 0) + ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY; + ax88179_led_setting(dev); ax179_data->eee_enabled = 0; @@ -1708,7 +1668,7 @@ static int ax88179_reset(struct usbnet *dev) ax88179_disable_eee(dev); ax88179_ethtool_get_eee(dev, &eee_data); - eee_data.advertised = 0; + linkmode_zero(eee_data.advertised); ax88179_ethtool_set_eee(dev, &eee_data); /* Restart autoneg */ @@ -1719,6 +1679,27 @@ static int ax88179_reset(struct usbnet *dev) return 0; } +static int ax88179_net_reset(struct usbnet *dev) +{ + u16 tmp16; + + ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PHYSR, + 2, &tmp16); + if (tmp16) { + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); + if (!(tmp16 & AX_MEDIUM_RECEIVE_EN)) { + tmp16 |= AX_MEDIUM_RECEIVE_EN; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); + } + } else { + ax88179_reset(dev); + } + + return 0; +} + static int ax88179_stop(struct usbnet *dev) { u16 tmp16; @@ -1738,7 +1719,7 @@ static const struct driver_info ax88179_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1751,7 +1732,7 @@ static const struct driver_info ax88178a_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1764,7 +1745,7 @@ static const struct driver_info cypress_GX3_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1777,7 +1758,7 @@ static const struct driver_info dlink_dub1312_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1790,7 +1771,7 @@ static const struct driver_info sitecom_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1803,7 +1784,7 @@ static const struct driver_info samsung_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1816,7 +1797,7 @@ static const struct driver_info lenovo_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1829,7 +1810,7 @@ static const struct driver_info belkin_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1842,7 +1823,7 @@ static const struct driver_info toshiba_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, @@ -1855,54 +1836,105 @@ static const struct driver_info mct_info = { .unbind = ax88179_unbind, .status = ax88179_status, .link_reset = ax88179_link_reset, - .reset = ax88179_reset, + .reset = ax88179_net_reset, .stop = ax88179_stop, .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; +static const struct driver_info at_umc2000_info = { + .description = "AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_net_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info at_umc200_info = { + .description = "AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_net_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info at_umc2000sp_info = { + .description = "AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_net_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + static const struct usb_device_id products[] = { { /* ASIX AX88179 10/100/1000 */ - USB_DEVICE(0x0b95, 0x1790), + USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0), .driver_info = (unsigned long)&ax88179_info, }, { /* ASIX AX88178A 10/100/1000 */ - USB_DEVICE(0x0b95, 0x178a), + USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x178a, 0xff, 0xff, 0), .driver_info = (unsigned long)&ax88178a_info, }, { /* Cypress GX3 SuperSpeed to Gigabit Ethernet Bridge Controller */ - USB_DEVICE(0x04b4, 0x3610), + USB_DEVICE_AND_INTERFACE_INFO(0x04b4, 0x3610, 0xff, 0xff, 0), .driver_info = (unsigned long)&cypress_GX3_info, }, { /* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */ - USB_DEVICE(0x2001, 0x4a00), + USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x4a00, 0xff, 0xff, 0), .driver_info = (unsigned long)&dlink_dub1312_info, }, { /* Sitecom USB 3.0 to Gigabit Adapter */ - USB_DEVICE(0x0df6, 0x0072), + USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0072, 0xff, 0xff, 0), .driver_info = (unsigned long)&sitecom_info, }, { /* Samsung USB Ethernet Adapter */ - USB_DEVICE(0x04e8, 0xa100), + USB_DEVICE_AND_INTERFACE_INFO(0x04e8, 0xa100, 0xff, 0xff, 0), .driver_info = (unsigned long)&samsung_info, }, { /* Lenovo OneLinkDock Gigabit LAN */ - USB_DEVICE(0x17ef, 0x304b), + USB_DEVICE_AND_INTERFACE_INFO(0x17ef, 0x304b, 0xff, 0xff, 0), .driver_info = (unsigned long)&lenovo_info, }, { /* Belkin B2B128 USB 3.0 Hub + Gigabit Ethernet Adapter */ - USB_DEVICE(0x050d, 0x0128), + USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x0128, 0xff, 0xff, 0), .driver_info = (unsigned long)&belkin_info, }, { /* Toshiba USB 3.0 GBit Ethernet Adapter */ - USB_DEVICE(0x0930, 0x0a13), + USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x0a13, 0xff, 0xff, 0), .driver_info = (unsigned long)&toshiba_info, }, { /* Magic Control Technology U3-A9003 USB 3.0 Gigabit Ethernet Adapter */ - USB_DEVICE(0x0711, 0x0179), + USB_DEVICE_AND_INTERFACE_INFO(0x0711, 0x0179, 0xff, 0xff, 0), .driver_info = (unsigned long)&mct_info, +}, { + /* Allied Telesis AT-UMC2000 USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */ + USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000e, 0xff, 0xff, 0), + .driver_info = (unsigned long)&at_umc2000_info, +}, { + /* Allied Telesis AT-UMC200 USB 3.0/USB 3.1 Gen 1 to Fast Ethernet Adapter */ + USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x000f, 0xff, 0xff, 0), + .driver_info = (unsigned long)&at_umc200_info, +}, { + /* Allied Telesis AT-UMC2000/SP USB 3.0/USB 3.1 Gen 1 to Gigabit Ethernet Adapter */ + USB_DEVICE_AND_INTERFACE_INFO(0x07c9, 0x0010, 0xff, 0xff, 0), + .driver_info = (unsigned long)&at_umc2000sp_info, }, { }, }; @@ -1915,7 +1947,7 @@ static struct usb_driver ax88179_178a_driver = { .suspend = ax88179_suspend, .resume = ax88179_resume, .reset_resume = ax88179_resume, - .disconnect = usbnet_disconnect, + .disconnect = ax88179_disconnect, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; |
