]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/nvidia/forcedeth.c
forcedeth: prevent TX timeouts after reboot
[karo-tx-linux.git] / drivers / net / ethernet / nvidia / forcedeth.c
index 928913c4f3ff291eb035a753dd0e4797bc8032cc..876beceaf2d7154f07d9de46bbb75fba3b1f0c43 100644 (file)
@@ -3218,7 +3218,7 @@ static void nv_force_linkspeed(struct net_device *dev, int speed, int duplex)
 }
 
 /**
- * nv_update_linkspeed: Setup the MAC according to the link partner
+ * nv_update_linkspeed - Setup the MAC according to the link partner
  * @dev: Network device to be configured
  *
  * The function queries the PHY and checks if there is a link partner.
@@ -3409,7 +3409,7 @@ set_speed:
 
        pause_flags = 0;
        /* setup pause frame */
-       if (np->duplex != 0) {
+       if (netif_running(dev) && (np->duplex != 0)) {
                if (np->autoneg && np->pause_flags & NV_PAUSEFRAME_AUTONEG) {
                        adv_pause = adv & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
                        lpa_pause = lpa & (LPA_PAUSE_CAP | LPA_PAUSE_ASYM);
@@ -3552,8 +3552,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
        return IRQ_HANDLED;
 }
 
-/**
- * All _optimized functions are used to help increase performance
+/* All _optimized functions are used to help increase performance
  * (reduce CPU and increase throughput). They use descripter version 3,
  * compiler directives, and reduce memory accesses.
  */
@@ -3776,7 +3775,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
                        np->link_timeout = jiffies + LINK_TIMEOUT;
                }
                if (events & NVREG_IRQ_RECOVER_ERROR) {
-                       spin_lock_irq(&np->lock);
+                       spin_lock_irqsave(&np->lock, flags);
                        /* disable interrupts on the nic */
                        writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
                        pci_push(base);
@@ -3786,7 +3785,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
                                np->recover_error = 1;
                                mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
                        }
-                       spin_unlock_irq(&np->lock);
+                       spin_unlock_irqrestore(&np->lock, flags);
                        break;
                }
                if (unlikely(i > max_interrupt_work)) {
@@ -4436,7 +4435,7 @@ static void nv_get_regs(struct net_device *dev, struct ethtool_regs *regs, void
 
        regs->version = FORCEDETH_REGS_VER;
        spin_lock_irq(&np->lock);
-       for (i = 0; i <= np->register_size/sizeof(u32); i++)
+       for (i = 0; i < np->register_size/sizeof(u32); i++)
                rbuf[i] = readl(base + i*sizeof(u32));
        spin_unlock_irq(&np->lock);
 }
@@ -5183,6 +5182,7 @@ static const struct ethtool_ops ops = {
        .get_ethtool_stats = nv_get_ethtool_stats,
        .get_sset_count = nv_get_sset_count,
        .self_test = nv_self_test,
+       .get_ts_info = ethtool_op_get_ts_info,
 };
 
 /* The mgmt unit and driver use a semaphore to access the phy during init */
@@ -5455,6 +5455,7 @@ static int nv_close(struct net_device *dev)
 
        netif_stop_queue(dev);
        spin_lock_irq(&np->lock);
+       nv_update_pause(dev, 0); /* otherwise stop_tx bricks NIC */
        nv_stop_rxtx(dev);
        nv_txrx_reset(dev);
 
@@ -5904,11 +5905,19 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
                goto out_error;
        }
 
+       netif_carrier_off(dev);
+
+       /* Some NICs freeze when TX pause is enabled while NIC is
+        * down, and this stays across warm reboots. The sequence
+        * below should be enough to recover from that state.
+        */
+       nv_update_pause(dev, 0);
+       nv_start_tx(dev);
+       nv_stop_tx(dev);
+
        if (id->driver_data & DEV_HAS_VLAN)
                nv_vlan_mode(dev, dev->features);
 
-       netif_carrier_off(dev);
-
        dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
                 dev->name, np->phy_oui, np->phyaddr, dev->dev_addr);