diff options
Diffstat (limited to 'drivers/net/usb/lan78xx.c')
-rw-r--r-- | drivers/net/usb/lan78xx.c | 252 |
1 files changed, 239 insertions, 13 deletions
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 705c180163c5..f20890ee03f3 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.3" +#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,6 +334,7 @@ struct lan78xx_net { unsigned maxpacket; struct timer_list delay; + struct timer_list stat_monitor; unsigned long data[5]; @@ -284,6 +347,9 @@ struct lan78xx_net { int fc_autoneg; u8 fc_request_control; + + int delta; + struct statstage stats; }; /* use ethtool to change the level for any given device */ @@ -382,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) { @@ -967,6 +1120,8 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) return -EIO; phy_mac_interrupt(phydev, 0); + + del_timer(&dev->stat_monitor); } else if (phydev->link && !dev->link_on) { dev->link_on = true; @@ -1007,6 +1162,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ret = lan78xx_update_flowcontrol(dev, ecmd.duplex, ladv, radv); 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; @@ -1099,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; + lan78xx_update_stats(dev); - 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]; - } - - 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, @@ -2095,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); @@ -2122,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); @@ -2166,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; @@ -2910,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); @@ -2984,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) @@ -3074,6 +3276,15 @@ static const struct net_device_ops lan78xx_netdev_ops = { .ndo_vlan_rx_kill_vid = lan78xx_vlan_rx_kill_vid, }; +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) { @@ -3120,6 +3331,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; @@ -3397,6 +3615,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); @@ -3457,6 +3677,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)) |