diff options
Diffstat (limited to 'drivers/net/usb/lan78xx.c')
| -rw-r--r-- | drivers/net/usb/lan78xx.c | 48 | 
1 files changed, 40 insertions, 8 deletions
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index f20890ee03f3..6a9d474b08b2 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -269,6 +269,7 @@ struct skb_data {		/* skb->cb is one of these */  	struct lan78xx_net *dev;  	enum skb_state state;  	size_t length; +	int num_of_packet;  };  struct usb_context { @@ -1803,7 +1804,34 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev)  static void lan78xx_link_status_change(struct net_device *net)  { -	/* nothing to do */ +	struct phy_device *phydev = net->phydev; +	int ret, temp; + +	/* At forced 100 F/H mode, chip may fail to set mode correctly +	 * when cable is switched between long(~50+m) and short one. +	 * As workaround, set to 10 before setting to 100 +	 * at forced 100 F/H mode. +	 */ +	if (!phydev->autoneg && (phydev->speed == 100)) { +		/* disable phy interrupt */ +		temp = phy_read(phydev, LAN88XX_INT_MASK); +		temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; +		ret = phy_write(phydev, LAN88XX_INT_MASK, temp); + +		temp = phy_read(phydev, MII_BMCR); +		temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); +		phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ +		temp |= BMCR_SPEED100; +		phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ + +		/* clear pending interrupt generated while workaround */ +		temp = phy_read(phydev, LAN88XX_INT_STS); + +		/* enable phy interrupt back */ +		temp = phy_read(phydev, LAN88XX_INT_MASK); +		temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; +		ret = phy_write(phydev, LAN88XX_INT_MASK, temp); +	}  }  static int lan78xx_phy_init(struct lan78xx_net *dev) @@ -2464,7 +2492,7 @@ static void tx_complete(struct urb *urb)  	struct lan78xx_net *dev = entry->dev;  	if (urb->status == 0) { -		dev->net->stats.tx_packets++; +		dev->net->stats.tx_packets += entry->num_of_packet;  		dev->net->stats.tx_bytes += entry->length;  	} else {  		dev->net->stats.tx_errors++; @@ -2681,10 +2709,11 @@ void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb)  		return;  	} -	skb->protocol = eth_type_trans(skb, dev->net);  	dev->net->stats.rx_packets++;  	dev->net->stats.rx_bytes += skb->len; +	skb->protocol = eth_type_trans(skb, dev->net); +  	netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",  		  skb->len + sizeof(struct ethhdr), skb->protocol);  	memset(skb->cb, 0, sizeof(struct skb_data)); @@ -2934,13 +2963,16 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev)  	skb_totallen = 0;  	pkt_cnt = 0; +	count = 0; +	length = 0;  	for (skb = tqp->next; pkt_cnt < tqp->qlen; skb = skb->next) {  		if (skb_is_gso(skb)) {  			if (pkt_cnt) {  				/* handle previous packets first */  				break;  			} -			length = skb->len; +			count = 1; +			length = skb->len - TX_OVERHEAD;  			skb2 = skb_dequeue(tqp);  			goto gso_skb;  		} @@ -2961,14 +2993,13 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev)  	for (count = pos = 0; count < pkt_cnt; count++) {  		skb2 = skb_dequeue(tqp);  		if (skb2) { +			length += (skb2->len - TX_OVERHEAD);  			memcpy(skb->data + pos, skb2->data, skb2->len);  			pos += roundup(skb2->len, sizeof(u32));  			dev_kfree_skb(skb2);  		}  	} -	length = skb_totallen; -  gso_skb:  	urb = usb_alloc_urb(0, GFP_ATOMIC);  	if (!urb) { @@ -2980,6 +3011,7 @@ gso_skb:  	entry->urb = urb;  	entry->dev = dev;  	entry->length = length; +	entry->num_of_packet = count;  	spin_lock_irqsave(&dev->txq.lock, flags);  	ret = usb_autopm_get_interface_async(dev->intf); @@ -3013,7 +3045,7 @@ gso_skb:  	ret = usb_submit_urb(urb, GFP_ATOMIC);  	switch (ret) {  	case 0: -		dev->net->trans_start = jiffies; +		netif_trans_update(dev->net);  		lan78xx_queue_skb(&dev->txq, skb, tx_start);  		if (skb_queue_len(&dev->txq) >= dev->tx_qlen)  			netif_stop_queue(dev->net); @@ -3697,7 +3729,7 @@ int lan78xx_resume(struct usb_interface *intf)  				usb_free_urb(res);  				usb_autopm_put_interface_async(dev->intf);  			} else { -				dev->net->trans_start = jiffies; +				netif_trans_update(dev->net);  				lan78xx_queue_skb(&dev->txq, skb, tx_start);  			}  		}  | 
