From 680abbd27d5acb0f504dd7f071f63f24b1164b21 Mon Sep 17 00:00:00 2001 From: Andrew Gospodarek Date: Fri, 11 Apr 2008 16:24:23 -0400 Subject: [PATCH] e1000e msi test and switch to intx RHBZ 290701 Another e1000e patch that is not upstream that will test to see if MSI works and if it doesn't seem to work, will switch back to INTx mode. --- drivers/net/e1000e/defines.h | 1 + drivers/net/e1000e/e1000.h | 1 + drivers/net/e1000e/netdev.c | 161 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 8 deletions(-) diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h index 6232c3e..f76f306 100644 --- a/drivers/net/e1000e/defines.h +++ b/drivers/net/e1000e/defines.h @@ -385,6 +385,7 @@ /* Interrupt Cause Set */ #define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ #define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ /* Transmit Descriptor Control */ diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index 176e090..df3bfad 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -309,6 +309,7 @@ struct e1000_info { #define FLAG_MSI_ENABLED (1 << 27) #define FLAG_RX_CSUM_ENABLED (1 << 28) #define FLAG_TSO_FORCE (1 << 29) +#define FLAG_MSI_TEST_FAILED (1 << 30) #define E1000_RX_DESC_PS(R, i) \ (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 55afb38..a574253 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -941,25 +941,30 @@ static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs) 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) { ndev_err(netdev, "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; + } } return err; @@ -2272,6 +2277,135 @@ err: } /** + * 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 pt_regs *regs) +{ + struct net_device *netdev = data; + struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + + u32 icr = er32(ICR); + ndev_dbg(netdev, "icr is %08X\n", 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); + + 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; + } + + /* Assume that the test fails, if it succeeds then the test + * MSI irq handler will unset this flag */ + adapter->flags |= FLAG_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; + ndev_dbg(netdev, "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 */ + ndev_dbg(netdev, "MSI interrupt test succeeded!\n"); +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 */ + ndev_warn(adapter->netdev, "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 * @@ -2326,6 +2460,17 @@ 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) { + ndev_err(netdev, "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); -- 1.5.2.1