|
@@ -1236,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;
|
|
@@ -2594,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
|
|
@@ -2652,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);
|
|
|
|