Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[deliverable/linux.git] / drivers / net / usb / lan78xx.c
index f20890ee03f33368fd68c6b5fb82f8fd76fa4310..f64778ad97535118d5b0705c1a5afa04ab57677c 100644 (file)
@@ -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);
This page took 0.035351 seconds and 5 git commands to generate.