diff options
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/usb/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/usb/ax88172a.c | 1 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 26 | ||||
-rw-r--r-- | drivers/net/usb/lan78xx.c | 528 | ||||
-rw-r--r-- | drivers/net/usb/lan78xx.h | 1 | ||||
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 11 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 7 |
8 files changed, 497 insertions, 89 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 7f83504dfa69..cdde59089f72 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -395,6 +395,10 @@ config USB_NET_RNDIS_HOST The protocol specification is incomplete, and is controlled by (and for) Microsoft; it isn't an "Open" ecosystem or market. +config USB_NET_CDC_SUBSET_ENABLE + tristate + depends on USB_NET_CDC_SUBSET + config USB_NET_CDC_SUBSET tristate "Simple USB Network Links (CDC Ethernet subset)" depends on USB_USBNET @@ -413,6 +417,7 @@ config USB_NET_CDC_SUBSET config USB_ALI_M5632 bool "ALi M5632 based 'USB 2.0 Data Link' cables" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE help Choose this option if you're using a host-to-host cable based on this design, which supports USB 2.0 high speed. @@ -420,6 +425,7 @@ config USB_ALI_M5632 config USB_AN2720 bool "AnchorChips 2720 based cables (Xircom PGUNET, ...)" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE help Choose this option if you're using a host-to-host cable based on this design. Note that AnchorChips is now a @@ -428,6 +434,7 @@ config USB_AN2720 config USB_BELKIN bool "eTEK based host-to-host cables (Advance, Belkin, ...)" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE default y help Choose this option if you're using a host-to-host cable @@ -437,6 +444,7 @@ config USB_BELKIN config USB_ARMLINUX bool "Embedded ARM Linux links (iPaq, ...)" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE default y help Choose this option to support the "usb-eth" networking driver @@ -454,6 +462,7 @@ config USB_ARMLINUX config USB_EPSON2888 bool "Epson 2888 based firmware (DEVELOPMENT)" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE help Choose this option to support the usb networking links used by some sample firmware from Epson. @@ -461,6 +470,7 @@ config USB_EPSON2888 config USB_KC2190 bool "KT Technology KC2190 based cables (InstaNet)" depends on USB_NET_CDC_SUBSET + select USB_NET_CDC_SUBSET_ENABLE help Choose this option if you're using a host-to-host cable with one of these chips. diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index b5f04068dbe4..37fb46aee341 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -23,7 +23,7 @@ obj-$(CONFIG_USB_NET_GL620A) += gl620a.o obj-$(CONFIG_USB_NET_NET1080) += net1080.o obj-$(CONFIG_USB_NET_PLUSB) += plusb.o obj-$(CONFIG_USB_NET_RNDIS_HOST) += rndis_host.o -obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o +obj-$(CONFIG_USB_NET_CDC_SUBSET_ENABLE) += cdc_subset.o obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o obj-$(CONFIG_USB_USBNET) += usbnet.o diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 224e7d82de6d..cf77f2dffa69 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -134,7 +134,6 @@ static void ax88172a_remove_mdio(struct usbnet *dev) netdev_info(dev->net, "deregistering mdio bus %s\n", priv->mdio->id); mdiobus_unregister(priv->mdio); - kfree(priv->mdio->irq); mdiobus_free(priv->mdio); } diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index dc0212c3cc28..86ba30ba35e8 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -837,7 +837,11 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; - /* reset data interface */ + /* Reset data interface. Some devices will not reset properly + * unless they are configured first. Toggle the altsetting to + * force a reset + */ + usb_set_interface(dev->udev, iface_no, data_altsetting); temp = usb_set_interface(dev->udev, iface_no, 0); if (temp) { dev_dbg(&intf->dev, "set interface failed\n"); @@ -984,8 +988,6 @@ EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) { - int ret; - /* MBIM backwards compatible function? */ if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM) return -ENODEV; @@ -994,16 +996,7 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) * Additionally, generic NCM devices are assumed to accept arbitrarily * placed NDP. */ - ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0); - - /* - * We should get an event when network connection is "connected" or - * "disconnected". Set network connection in "disconnected" state - * (carrier is OFF) during attach, so the IP network stack does not - * start IPv6 negotiation and more. - */ - usbnet_link_change(dev, 0, 0); - return ret; + return cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0); } static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max) @@ -1586,7 +1579,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) static const struct driver_info cdc_ncm_info = { .description = "CDC NCM", - .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET, + .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET + | FLAG_LINK_INTR, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, .manage_power = usbnet_manage_power, @@ -1599,7 +1593,7 @@ static const struct driver_info cdc_ncm_info = { static const struct driver_info wwan_info = { .description = "Mobile Broadband Network Device", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET - | FLAG_WWAN, + | FLAG_LINK_INTR | FLAG_WWAN, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, .manage_power = usbnet_manage_power, @@ -1612,7 +1606,7 @@ static const struct driver_info wwan_info = { static const struct driver_info wwan_noarp_info = { .description = "Mobile Broadband Network Device (NO ARP)", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET - | FLAG_WWAN | FLAG_NOARP, + | FLAG_LINK_INTR | FLAG_WWAN | FLAG_NOARP, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, .manage_power = usbnet_manage_power, diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 2ed53331bfb2..d36d5ebf37f3 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -36,7 +36,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.1" +#define DRIVER_VERSION "1.0.4" #define TX_TIMEOUT_JIFFIES (5 * HZ) #define THROTTLE_JIFFIES (HZ / 8) @@ -86,6 +86,9 @@ /* default autosuspend delay (mSec)*/ #define DEFAULT_AUTOSUSPEND_DELAY (10 * 1000) +/* statistic update interval (mSec) */ +#define STAT_UPDATE_TIMER (1 * 1000) + static const char lan78xx_gstrings[][ETH_GSTRING_LEN] = { "RX FCS Errors", "RX Alignment Errors", @@ -186,6 +189,56 @@ struct lan78xx_statstage { u32 eee_tx_lpi_time; }; +struct lan78xx_statstage64 { + u64 rx_fcs_errors; + u64 rx_alignment_errors; + u64 rx_fragment_errors; + u64 rx_jabber_errors; + u64 rx_undersize_frame_errors; + u64 rx_oversize_frame_errors; + u64 rx_dropped_frames; + u64 rx_unicast_byte_count; + u64 rx_broadcast_byte_count; + u64 rx_multicast_byte_count; + u64 rx_unicast_frames; + u64 rx_broadcast_frames; + u64 rx_multicast_frames; + u64 rx_pause_frames; + u64 rx_64_byte_frames; + u64 rx_65_127_byte_frames; + u64 rx_128_255_byte_frames; + u64 rx_256_511_bytes_frames; + u64 rx_512_1023_byte_frames; + u64 rx_1024_1518_byte_frames; + u64 rx_greater_1518_byte_frames; + u64 eee_rx_lpi_transitions; + u64 eee_rx_lpi_time; + u64 tx_fcs_errors; + u64 tx_excess_deferral_errors; + u64 tx_carrier_errors; + u64 tx_bad_byte_count; + u64 tx_single_collisions; + u64 tx_multiple_collisions; + u64 tx_excessive_collision; + u64 tx_late_collisions; + u64 tx_unicast_byte_count; + u64 tx_broadcast_byte_count; + u64 tx_multicast_byte_count; + u64 tx_unicast_frames; + u64 tx_broadcast_frames; + u64 tx_multicast_frames; + u64 tx_pause_frames; + u64 tx_64_byte_frames; + u64 tx_65_127_byte_frames; + u64 tx_128_255_byte_frames; + u64 tx_256_511_bytes_frames; + u64 tx_512_1023_byte_frames; + u64 tx_1024_1518_byte_frames; + u64 tx_greater_1518_byte_frames; + u64 eee_tx_lpi_transitions; + u64 eee_tx_lpi_time; +}; + struct lan78xx_net; struct lan78xx_priv { @@ -232,6 +285,15 @@ struct usb_context { #define EVENT_DEV_WAKING 6 #define EVENT_DEV_ASLEEP 7 #define EVENT_DEV_OPEN 8 +#define EVENT_STAT_UPDATE 9 + +struct statstage { + struct mutex access_lock; /* for stats access */ + struct lan78xx_statstage saved; + struct lan78xx_statstage rollover_count; + struct lan78xx_statstage rollover_max; + struct lan78xx_statstage64 curr_stat; +}; struct lan78xx_net { struct net_device *net; @@ -272,14 +334,22 @@ struct lan78xx_net { unsigned maxpacket; struct timer_list delay; + struct timer_list stat_monitor; unsigned long data[5]; int link_on; u8 mdix_ctrl; - u32 devid; + u32 chipid; + u32 chiprev; struct mii_bus *mdiobus; + + int fc_autoneg; + u8 fc_request_control; + + int delta; + struct statstage stats; }; /* use ethtool to change the level for any given device */ @@ -378,6 +448,93 @@ static int lan78xx_read_stats(struct lan78xx_net *dev, return ret; } +#define check_counter_rollover(struct1, dev_stats, member) { \ + if (struct1->member < dev_stats.saved.member) \ + dev_stats.rollover_count.member++; \ + } + +static void lan78xx_check_stat_rollover(struct lan78xx_net *dev, + struct lan78xx_statstage *stats) +{ + check_counter_rollover(stats, dev->stats, rx_fcs_errors); + check_counter_rollover(stats, dev->stats, rx_alignment_errors); + check_counter_rollover(stats, dev->stats, rx_fragment_errors); + check_counter_rollover(stats, dev->stats, rx_jabber_errors); + check_counter_rollover(stats, dev->stats, rx_undersize_frame_errors); + check_counter_rollover(stats, dev->stats, rx_oversize_frame_errors); + check_counter_rollover(stats, dev->stats, rx_dropped_frames); + check_counter_rollover(stats, dev->stats, rx_unicast_byte_count); + check_counter_rollover(stats, dev->stats, rx_broadcast_byte_count); + check_counter_rollover(stats, dev->stats, rx_multicast_byte_count); + check_counter_rollover(stats, dev->stats, rx_unicast_frames); + check_counter_rollover(stats, dev->stats, rx_broadcast_frames); + check_counter_rollover(stats, dev->stats, rx_multicast_frames); + check_counter_rollover(stats, dev->stats, rx_pause_frames); + check_counter_rollover(stats, dev->stats, rx_64_byte_frames); + check_counter_rollover(stats, dev->stats, rx_65_127_byte_frames); + check_counter_rollover(stats, dev->stats, rx_128_255_byte_frames); + check_counter_rollover(stats, dev->stats, rx_256_511_bytes_frames); + check_counter_rollover(stats, dev->stats, rx_512_1023_byte_frames); + check_counter_rollover(stats, dev->stats, rx_1024_1518_byte_frames); + check_counter_rollover(stats, dev->stats, rx_greater_1518_byte_frames); + check_counter_rollover(stats, dev->stats, eee_rx_lpi_transitions); + check_counter_rollover(stats, dev->stats, eee_rx_lpi_time); + check_counter_rollover(stats, dev->stats, tx_fcs_errors); + check_counter_rollover(stats, dev->stats, tx_excess_deferral_errors); + check_counter_rollover(stats, dev->stats, tx_carrier_errors); + check_counter_rollover(stats, dev->stats, tx_bad_byte_count); + check_counter_rollover(stats, dev->stats, tx_single_collisions); + check_counter_rollover(stats, dev->stats, tx_multiple_collisions); + check_counter_rollover(stats, dev->stats, tx_excessive_collision); + check_counter_rollover(stats, dev->stats, tx_late_collisions); + check_counter_rollover(stats, dev->stats, tx_unicast_byte_count); + check_counter_rollover(stats, dev->stats, tx_broadcast_byte_count); + check_counter_rollover(stats, dev->stats, tx_multicast_byte_count); + check_counter_rollover(stats, dev->stats, tx_unicast_frames); + check_counter_rollover(stats, dev->stats, tx_broadcast_frames); + check_counter_rollover(stats, dev->stats, tx_multicast_frames); + check_counter_rollover(stats, dev->stats, tx_pause_frames); + check_counter_rollover(stats, dev->stats, tx_64_byte_frames); + check_counter_rollover(stats, dev->stats, tx_65_127_byte_frames); + check_counter_rollover(stats, dev->stats, tx_128_255_byte_frames); + check_counter_rollover(stats, dev->stats, tx_256_511_bytes_frames); + check_counter_rollover(stats, dev->stats, tx_512_1023_byte_frames); + check_counter_rollover(stats, dev->stats, tx_1024_1518_byte_frames); + check_counter_rollover(stats, dev->stats, tx_greater_1518_byte_frames); + check_counter_rollover(stats, dev->stats, eee_tx_lpi_transitions); + check_counter_rollover(stats, dev->stats, eee_tx_lpi_time); + + memcpy(&dev->stats.saved, stats, sizeof(struct lan78xx_statstage)); +} + +static void lan78xx_update_stats(struct lan78xx_net *dev) +{ + u32 *p, *count, *max; + u64 *data; + int i; + struct lan78xx_statstage lan78xx_stats; + + if (usb_autopm_get_interface(dev->intf) < 0) + return; + + p = (u32 *)&lan78xx_stats; + count = (u32 *)&dev->stats.rollover_count; + max = (u32 *)&dev->stats.rollover_max; + data = (u64 *)&dev->stats.curr_stat; + + mutex_lock(&dev->stats.access_lock); + + if (lan78xx_read_stats(dev, &lan78xx_stats) > 0) + lan78xx_check_stat_rollover(dev, &lan78xx_stats); + + for (i = 0; i < (sizeof(lan78xx_stats) / (sizeof(u32))); i++) + data[i] = (u64)p[i] + ((u64)count[i] * ((u64)max[i] + 1)); + + mutex_unlock(&dev->stats.access_lock); + + usb_autopm_put_interface(dev->intf); +} + /* Loop until the read is completed with timeout called with phy_mutex held */ static int lan78xx_phy_wait_not_busy(struct lan78xx_net *dev) { @@ -462,32 +619,53 @@ static int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset, u32 length, u8 *data) { u32 val; + u32 saved; int i, ret; + int retval; - ret = lan78xx_eeprom_confirm_not_busy(dev); - if (ret) - return ret; + /* depends on chip, some EEPROM pins are muxed with LED function. + * disable & restore LED function to access EEPROM. + */ + ret = lan78xx_read_reg(dev, HW_CFG, &val); + saved = val; + 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); + } + + retval = lan78xx_eeprom_confirm_not_busy(dev); + if (retval) + return retval; for (i = 0; i < length; i++) { val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_; val |= (offset & E2P_CMD_EPC_ADDR_MASK_); ret = lan78xx_write_reg(dev, E2P_CMD, val); - if (unlikely(ret < 0)) - return -EIO; + if (unlikely(ret < 0)) { + retval = -EIO; + goto exit; + } - ret = lan78xx_wait_eeprom(dev); - if (ret < 0) - return ret; + retval = lan78xx_wait_eeprom(dev); + if (retval < 0) + goto exit; ret = lan78xx_read_reg(dev, E2P_DATA, &val); - if (unlikely(ret < 0)) - return -EIO; + if (unlikely(ret < 0)) { + retval = -EIO; + goto exit; + } data[i] = val & 0xFF; offset++; } - return 0; + retval = 0; +exit: + if (dev->chipid == ID_REV_CHIP_ID_7800_) + ret = lan78xx_write_reg(dev, HW_CFG, saved); + + return retval; } static int lan78xx_read_eeprom(struct lan78xx_net *dev, u32 offset, @@ -509,44 +687,67 @@ static int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset, u32 length, u8 *data) { u32 val; + u32 saved; int i, ret; + int retval; - ret = lan78xx_eeprom_confirm_not_busy(dev); - if (ret) - return ret; + /* depends on chip, some EEPROM pins are muxed with LED function. + * disable & restore LED function to access EEPROM. + */ + ret = lan78xx_read_reg(dev, HW_CFG, &val); + saved = val; + 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); + } + + retval = lan78xx_eeprom_confirm_not_busy(dev); + if (retval) + goto exit; /* Issue write/erase enable command */ val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_; ret = lan78xx_write_reg(dev, E2P_CMD, val); - if (unlikely(ret < 0)) - return -EIO; + if (unlikely(ret < 0)) { + retval = -EIO; + goto exit; + } - ret = lan78xx_wait_eeprom(dev); - if (ret < 0) - return ret; + retval = lan78xx_wait_eeprom(dev); + if (retval < 0) + goto exit; for (i = 0; i < length; i++) { /* Fill data register */ val = data[i]; ret = lan78xx_write_reg(dev, E2P_DATA, val); - if (ret < 0) - return ret; + if (ret < 0) { + retval = -EIO; + goto exit; + } /* Send "write" command */ val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_; val |= (offset & E2P_CMD_EPC_ADDR_MASK_); ret = lan78xx_write_reg(dev, E2P_CMD, val); - if (ret < 0) - return ret; + if (ret < 0) { + retval = -EIO; + goto exit; + } - ret = lan78xx_wait_eeprom(dev); - if (ret < 0) - return ret; + retval = lan78xx_wait_eeprom(dev); + if (retval < 0) + goto exit; offset++; } - return 0; + retval = 0; +exit: + if (dev->chipid == ID_REV_CHIP_ID_7800_) + ret = lan78xx_write_reg(dev, HW_CFG, saved); + + return retval; } static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset, @@ -857,11 +1058,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_; @@ -904,7 +1109,6 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) if (!phydev->link && dev->link_on) { dev->link_on = false; - netif_carrier_off(dev->net); /* reset MAC */ ret = lan78xx_read_reg(dev, MAC_CR, &buf); @@ -914,6 +1118,10 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ret = lan78xx_write_reg(dev, MAC_CR, buf); if (unlikely(ret < 0)) return -EIO; + + phy_mac_interrupt(phydev, 0); + + del_timer(&dev->stat_monitor); } else if (phydev->link && !dev->link_on) { dev->link_on = true; @@ -953,7 +1161,13 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ethtool_cmd_speed(&ecmd), ecmd.duplex, ladv, radv); ret = lan78xx_update_flowcontrol(dev, ecmd.duplex, ladv, radv); - netif_carrier_on(dev->net); + phy_mac_interrupt(phydev, 1); + + if (!timer_pending(&dev->stat_monitor)) { + dev->delta = 1; + mod_timer(&dev->stat_monitor, + jiffies + STAT_UPDATE_TIMER); + } } return ret; @@ -1046,20 +1260,12 @@ static void lan78xx_get_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct lan78xx_net *dev = netdev_priv(netdev); - struct lan78xx_statstage lan78xx_stat; - u32 *p; - int i; - - if (usb_autopm_get_interface(dev->intf) < 0) - return; - if (lan78xx_read_stats(dev, &lan78xx_stat) > 0) { - p = (u32 *)&lan78xx_stat; - for (i = 0; i < (sizeof(lan78xx_stat) / (sizeof(u32))); i++) - data[i] = p[i]; - } + lan78xx_update_stats(dev); - usb_autopm_put_interface(dev->intf); + mutex_lock(&dev->stats.access_lock); + memcpy(data, &dev->stats.curr_stat, sizeof(dev->stats.curr_stat)); + mutex_unlock(&dev->stats.access_lock); } static void lan78xx_get_wol(struct net_device *netdev, @@ -1340,6 +1546,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, @@ -1358,6 +1620,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) @@ -1495,7 +1759,6 @@ done: static int lan78xx_mdio_init(struct lan78xx_net *dev) { int ret; - int i; dev->mdiobus = mdiobus_alloc(); if (!dev->mdiobus) { @@ -1511,13 +1774,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); - /* 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: + 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; @@ -1550,6 +1809,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); @@ -1558,6 +1818,16 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) return -EIO; } + /* Enable PHY interrupts. + * We handle our own interrupt + */ + ret = phy_read(phydev, LAN88XX_INT_STS); + ret = phy_write(phydev, LAN88XX_INT_MASK, + LAN88XX_INT_MASK_MDINTPIN_EN_ | + LAN88XX_INT_MASK_LINK_CHANGE_); + + phydev->irq = PHY_IGNORE_INTERRUPT; + ret = phy_connect_direct(dev->net, phydev, lan78xx_link_status_change, PHY_INTERFACE_MODE_GMII); @@ -1572,22 +1842,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); - /* 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); + dev->fc_autoneg = phydev->autoneg; + phy_start(phydev); netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); @@ -1876,7 +2141,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); @@ -1982,6 +2248,32 @@ static int lan78xx_reset(struct lan78xx_net *dev) return 0; } +static void lan78xx_init_stats(struct lan78xx_net *dev) +{ + u32 *p; + int i; + + /* initialize for stats update + * some counters are 20bits and some are 32bits + */ + p = (u32 *)&dev->stats.rollover_max; + for (i = 0; i < (sizeof(dev->stats.rollover_max) / (sizeof(u32))); i++) + p[i] = 0xFFFFF; + + dev->stats.rollover_max.rx_unicast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.rx_broadcast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.rx_multicast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.eee_rx_lpi_transitions = 0xFFFFFFFF; + dev->stats.rollover_max.eee_rx_lpi_time = 0xFFFFFFFF; + dev->stats.rollover_max.tx_unicast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.tx_broadcast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.tx_multicast_byte_count = 0xFFFFFFFF; + dev->stats.rollover_max.eee_tx_lpi_transitions = 0xFFFFFFFF; + dev->stats.rollover_max.eee_tx_lpi_time = 0xFFFFFFFF; + + lan78xx_defer_kevent(dev, EVENT_STAT_UPDATE); +} + static int lan78xx_open(struct net_device *net) { struct lan78xx_net *dev = netdev_priv(net); @@ -2009,6 +2301,8 @@ static int lan78xx_open(struct net_device *net) } } + lan78xx_init_stats(dev); + set_bit(EVENT_DEV_OPEN, &dev->flags); netif_start_queue(net); @@ -2053,6 +2347,9 @@ int lan78xx_stop(struct net_device *net) { struct lan78xx_net *dev = netdev_priv(net); + if (timer_pending(&dev->stat_monitor)) + del_timer_sync(&dev->stat_monitor); + phy_stop(net->phydev); phy_disconnect(net->phydev); net->phydev = NULL; @@ -2221,7 +2518,9 @@ netdev_tx_t lan78xx_start_xmit(struct sk_buff *skb, struct net_device *net) if (skb2) { skb_queue_tail(&dev->txq_pend, skb2); - if (skb_queue_len(&dev->txq_pend) > 10) + /* throttle TX patch at slower than SUPER SPEED USB */ + if ((dev->udev->speed < USB_SPEED_SUPER) && + (skb_queue_len(&dev->txq_pend) > 10)) netif_stop_queue(net); } else { netif_dbg(dev, tx_err, dev->net, @@ -2795,6 +3094,13 @@ static void lan78xx_bh(unsigned long param) } if (netif_device_present(dev->net) && netif_running(dev->net)) { + /* reset update timer delta */ + if (timer_pending(&dev->stat_monitor) && (dev->delta != 1)) { + dev->delta = 1; + mod_timer(&dev->stat_monitor, + jiffies + STAT_UPDATE_TIMER); + } + if (!skb_queue_empty(&dev->txq_pend)) lan78xx_tx_bh(dev); @@ -2869,6 +3175,17 @@ skip_reset: usb_autopm_put_interface(dev->intf); } } + + if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) { + lan78xx_update_stats(dev); + + clear_bit(EVENT_STAT_UPDATE, &dev->flags); + + mod_timer(&dev->stat_monitor, + jiffies + (STAT_UPDATE_TIMER * dev->delta)); + + dev->delta = min((dev->delta * 2), 50); + } } static void intr_complete(struct urb *urb) @@ -2944,6 +3261,54 @@ void lan78xx_tx_timeout(struct net_device *net) tasklet_schedule(&dev->bh); } +struct rtnl_link_stats64 *lan78xx_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *storage) +{ + struct lan78xx_net *dev = netdev_priv(netdev); + struct lan78xx_statstage64 stats; + + /* curr_stat is updated by timer. + * periodic reading from HW will prevent from entering USB auto suspend. + * if autosuspend is disabled, read from HW. + */ + if (!dev->udev->dev.power.runtime_auto) + lan78xx_update_stats(dev); + + mutex_lock(&dev->stats.access_lock); + memcpy(&stats, &dev->stats.curr_stat, sizeof(stats)); + mutex_unlock(&dev->stats.access_lock); + + /* calc by driver */ + storage->rx_packets = (__u64)netdev->stats.rx_packets; + storage->tx_packets = (__u64)netdev->stats.tx_packets; + storage->rx_bytes = (__u64)netdev->stats.rx_bytes; + storage->tx_bytes = (__u64)netdev->stats.tx_bytes; + + /* use counter */ + storage->rx_length_errors = stats.rx_undersize_frame_errors + + stats.rx_oversize_frame_errors; + storage->rx_crc_errors = stats.rx_fcs_errors; + storage->rx_frame_errors = stats.rx_alignment_errors; + storage->rx_fifo_errors = stats.rx_dropped_frames; + storage->rx_over_errors = stats.rx_oversize_frame_errors; + storage->rx_errors = stats.rx_fcs_errors + + stats.rx_alignment_errors + + stats.rx_fragment_errors + + stats.rx_jabber_errors + + stats.rx_undersize_frame_errors + + stats.rx_oversize_frame_errors + + stats.rx_dropped_frames; + + storage->tx_carrier_errors = stats.tx_carrier_errors; + storage->tx_errors = stats.tx_fcs_errors + + stats.tx_excess_deferral_errors + + stats.tx_carrier_errors; + + storage->multicast = stats.rx_multicast_frames; + + return storage; +} + static const struct net_device_ops lan78xx_netdev_ops = { .ndo_open = lan78xx_open, .ndo_stop = lan78xx_stop, @@ -2957,8 +3322,18 @@ static const struct net_device_ops lan78xx_netdev_ops = { .ndo_set_features = lan78xx_set_features, .ndo_vlan_rx_add_vid = lan78xx_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = lan78xx_vlan_rx_kill_vid, + .ndo_get_stats64 = lan78xx_get_stats64, }; +static void lan78xx_stat_monitor(unsigned long param) +{ + struct lan78xx_net *dev; + + dev = (struct lan78xx_net *)param; + + lan78xx_defer_kevent(dev, EVENT_STAT_UPDATE); +} + static int lan78xx_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -3005,6 +3380,13 @@ static int lan78xx_probe(struct usb_interface *intf, netdev->watchdog_timeo = TX_TIMEOUT_JIFFIES; netdev->ethtool_ops = &lan78xx_ethtool_ops; + dev->stat_monitor.function = lan78xx_stat_monitor; + dev->stat_monitor.data = (unsigned long)dev; + dev->delta = 1; + init_timer(&dev->stat_monitor); + + mutex_init(&dev->stats.access_lock); + ret = lan78xx_bind(dev, intf); if (ret < 0) goto out2; @@ -3282,6 +3664,8 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) } if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { + del_timer(&dev->stat_monitor); + if (PMSG_IS_AUTO(message)) { /* auto suspend (selective suspend) */ ret = lan78xx_read_reg(dev, MAC_TX, &buf); @@ -3342,6 +3726,12 @@ int lan78xx_resume(struct usb_interface *intf) int ret; u32 buf; + if (!timer_pending(&dev->stat_monitor)) { + dev->delta = 1; + mod_timer(&dev->stat_monitor, + jiffies + STAT_UPDATE_TIMER); + } + if (!--dev->suspend_count) { /* resume interrupt URBs */ if (dev->urb_intr && test_bit(EVENT_DEV_OPEN, &dev->flags)) diff --git a/drivers/net/usb/lan78xx.h b/drivers/net/usb/lan78xx.h index a93fb653e7c5..40927906109a 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) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 23e9880791fc..7d717c66bcb0 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -637,6 +637,7 @@ static const struct usb_device_id products[] = { /* 3. Combined interface devices matching on interface number */ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ + {QMI_FIXED_INTF(0x05c6, 0x6001, 3)}, /* 4G LTE usb-modem U901 */ {QMI_FIXED_INTF(0x05c6, 0x7000, 0)}, {QMI_FIXED_INTF(0x05c6, 0x7001, 1)}, {QMI_FIXED_INTF(0x05c6, 0x7002, 1)}, @@ -860,8 +861,10 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */ {QMI_FIXED_INTF(0x1199, 0x9057, 8)}, {QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */ - {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */ - {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */ + {QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ @@ -878,12 +881,16 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)}, /* Olivetti Olicard 500 */ {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)}, /* Cinterion PLxx */ {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)}, /* Cinterion PHxx,PXxx */ + {QMI_FIXED_INTF(0x1e2d, 0x0082, 4)}, /* Cinterion PHxx,PXxx (2 RmNet) */ + {QMI_FIXED_INTF(0x1e2d, 0x0082, 5)}, /* Cinterion PHxx,PXxx (2 RmNet) */ + {QMI_FIXED_INTF(0x1e2d, 0x0083, 4)}, /* Cinterion PHxx,PXxx (1 RmNet + USB Audio)*/ {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ + {QMI_FIXED_INTF(0x413c, 0x81b3, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 0b0ba7ef14e4..10798128c03f 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1769,6 +1769,13 @@ out3: if (info->unbind) info->unbind (dev, udev); out1: + /* subdrivers must undo all they did in bind() if they + * fail it, but we may fail later and a deferred kevent + * may trigger an error resubmitting itself and, worse, + * schedule a timer. So we kill it all just in case. + */ + cancel_work_sync(&dev->kevent); + del_timer_sync(&dev->delay); free_netdev(net); out: return status; |