]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/e1000e/netdev.c
Merge branch 'x86/cpu' into x86/core
[linux-2.6-omap-h63xx.git] / drivers / net / e1000e / netdev.c
index 05b0b2f9c54bc7ac2234e4ef5dedb3a8469a2103..d266510c8a94683ae4d21516cf6c18491dc3eb3a 100644 (file)
@@ -510,9 +510,12 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
                            netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
                        if (new_skb) {
                                skb_reserve(new_skb, NET_IP_ALIGN);
-                               memcpy(new_skb->data - NET_IP_ALIGN,
-                                      skb->data - NET_IP_ALIGN,
-                                      length + NET_IP_ALIGN);
+                               skb_copy_to_linear_data_offset(new_skb,
+                                                              -NET_IP_ALIGN,
+                                                              (skb->data -
+                                                               NET_IP_ALIGN),
+                                                              (length +
+                                                               NET_IP_ALIGN));
                                /* save the skb in buffer_info as good */
                                buffer_info->skb = skb;
                                skb = new_skb;
@@ -1233,26 +1236,36 @@ static irqreturn_t e1000_intr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+/**
+ * e1000_request_irq - initialize interrupts
+ *
+ * Attempts to configure interrupts using the best available
+ * capabilities of the hardware and kernel.
+ **/
 static int e1000_request_irq(struct e1000_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
-       irq_handler_t handler = e1000_intr;
        int irq_flags = IRQF_SHARED;
        int err;
 
-       if (!pci_enable_msi(adapter->pdev)) {
-               adapter->flags |= FLAG_MSI_ENABLED;
-               handler = e1000_intr_msi;
-               irq_flags = 0;
+       if (!(adapter->flags & FLAG_MSI_TEST_FAILED)) {
+               err = pci_enable_msi(adapter->pdev);
+               if (!err) {
+                       adapter->flags |= FLAG_MSI_ENABLED;
+                       irq_flags = 0;
+               }
        }
 
-       err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name,
-                         netdev);
+       err = request_irq(adapter->pdev->irq,
+                         ((adapter->flags & FLAG_MSI_ENABLED) ?
+                               &e1000_intr_msi : &e1000_intr),
+                         irq_flags, netdev->name, netdev);
        if (err) {
-               e_err("Unable to allocate %s interrupt (return: %d)\n",
-                     adapter->flags & FLAG_MSI_ENABLED ? "MSI":"INTx", err);
-               if (adapter->flags & FLAG_MSI_ENABLED)
+               if (adapter->flags & FLAG_MSI_ENABLED) {
                        pci_disable_msi(adapter->pdev);
+                       adapter->flags &= ~FLAG_MSI_ENABLED;
+               }
+               e_err("Unable to allocate interrupt, Error: %d\n", err);
        }
 
        return err;
@@ -2591,6 +2604,135 @@ err:
        return -ENOMEM;
 }
 
+/**
+ * e1000_intr_msi_test - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ **/
+static irqreturn_t e1000_intr_msi_test(int irq, void *data)
+{
+       struct net_device *netdev = data;
+       struct e1000_adapter *adapter = netdev_priv(netdev);
+       struct e1000_hw *hw = &adapter->hw;
+       u32 icr = er32(ICR);
+
+       e_dbg("%s: icr is %08X\n", netdev->name, icr);
+       if (icr & E1000_ICR_RXSEQ) {
+               adapter->flags &= ~FLAG_MSI_TEST_FAILED;
+               wmb();
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * e1000_test_msi_interrupt - Returns 0 for successful test
+ * @adapter: board private struct
+ *
+ * code flow taken from tg3.c
+ **/
+static int e1000_test_msi_interrupt(struct e1000_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       struct e1000_hw *hw = &adapter->hw;
+       int err;
+
+       /* poll_enable hasn't been called yet, so don't need disable */
+       /* clear any pending events */
+       er32(ICR);
+
+       /* free the real vector and request a test handler */
+       e1000_free_irq(adapter);
+
+       /* Assume that the test fails, if it succeeds then the test
+        * MSI irq handler will unset this flag */
+       adapter->flags |= FLAG_MSI_TEST_FAILED;
+
+       err = pci_enable_msi(adapter->pdev);
+       if (err)
+               goto msi_test_failed;
+
+       err = request_irq(adapter->pdev->irq, &e1000_intr_msi_test, 0,
+                         netdev->name, netdev);
+       if (err) {
+               pci_disable_msi(adapter->pdev);
+               goto msi_test_failed;
+       }
+
+       wmb();
+
+       e1000_irq_enable(adapter);
+
+       /* fire an unusual interrupt on the test handler */
+       ew32(ICS, E1000_ICS_RXSEQ);
+       e1e_flush();
+       msleep(50);
+
+       e1000_irq_disable(adapter);
+
+       rmb();
+
+       if (adapter->flags & FLAG_MSI_TEST_FAILED) {
+               err = -EIO;
+               e_info("MSI interrupt test failed!\n");
+       }
+
+       free_irq(adapter->pdev->irq, netdev);
+       pci_disable_msi(adapter->pdev);
+
+       if (err == -EIO)
+               goto msi_test_failed;
+
+       /* okay so the test worked, restore settings */
+       e_dbg("%s: MSI interrupt test succeeded!\n", netdev->name);
+msi_test_failed:
+       /* restore the original vector, even if it failed */
+       e1000_request_irq(adapter);
+       return err;
+}
+
+/**
+ * e1000_test_msi - Returns 0 if MSI test succeeds or INTx mode is restored
+ * @adapter: board private struct
+ *
+ * code flow taken from tg3.c, called with e1000 interrupts disabled.
+ **/
+static int e1000_test_msi(struct e1000_adapter *adapter)
+{
+       int err;
+       u16 pci_cmd;
+
+       if (!(adapter->flags & FLAG_MSI_ENABLED))
+               return 0;
+
+       /* disable SERR in case the MSI write causes a master abort */
+       pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd);
+       pci_write_config_word(adapter->pdev, PCI_COMMAND,
+                             pci_cmd & ~PCI_COMMAND_SERR);
+
+       err = e1000_test_msi_interrupt(adapter);
+
+       /* restore previous setting of command word */
+       pci_write_config_word(adapter->pdev, PCI_COMMAND, pci_cmd);
+
+       /* success ! */
+       if (!err)
+               return 0;
+
+       /* EIO means MSI test failed */
+       if (err != -EIO)
+               return err;
+
+       /* back to INTx mode */
+       e_warn("MSI interrupt test failed, using legacy interrupt.\n");
+
+       e1000_free_irq(adapter);
+
+       err = e1000_request_irq(adapter);
+
+       return err;
+}
+
 /**
  * e1000_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -2649,6 +2791,19 @@ static int e1000_open(struct net_device *netdev)
        if (err)
                goto err_req_irq;
 
+       /*
+        * Work around PCIe errata with MSI interrupts causing some chipsets to
+        * ignore e1000e MSI messages, which means we need to test our MSI
+        * interrupt now
+        */
+       {
+               err = e1000_test_msi(adapter);
+               if (err) {
+                       e_err("Interrupt allocation failed\n");
+                       goto err_req_irq;
+               }
+       }
+
        /* From here on the code is the same as e1000e_up() */
        clear_bit(__E1000_DOWN, &adapter->state);
 
@@ -3055,7 +3210,7 @@ static void e1000_watchdog_task(struct work_struct *work)
                        case SPEED_10:
                                txb2b = 0;
                                netdev->tx_queue_len = 10;
-                               adapter->tx_timeout_factor = 14;
+                               adapter->tx_timeout_factor = 16;
                                break;
                        case SPEED_100:
                                txb2b = 0;
@@ -3721,7 +3876,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
        struct e1000_adapter *adapter = netdev_priv(netdev);
        int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
 
-       if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+       if ((new_mtu < ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN) ||
            (max_frame > MAX_JUMBO_FRAME_SIZE)) {
                e_err("Invalid MTU setting\n");
                return -EINVAL;