From eeb2bf5e21bf7ac4b8377fcb8c4cc34e2016978c Mon Sep 17 00:00:00 2001 From: Andrew Gospodarek Date: Fri, 11 Apr 2008 16:19:54 -0400 Subject: [PATCH] e1000 msi test and switch to intx RHBZ 290701 Another e1000 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/e1000/e1000_main.c | 143 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 141 insertions(+), 2 deletions(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 84fc318..7f033ce 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -36,7 +36,7 @@ static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; #else #define DRIVERNAPI "-NAPI" #endif -#define DRV_VERSION "7.3.20-k2"DRIVERNAPI +#define DRV_VERSION "7.3.20-k3"DRIVERNAPI const char e1000_driver_version[] = DRV_VERSION; static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; @@ -324,7 +324,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter) int irq_flags = IRQF_SHARED; int err; - if (adapter->hw.mac_type >= e1000_82571) { + if (adapter->hw.mac_type >= e1000_82571 && adapter->have_msi) { adapter->have_msi = !pci_enable_msi(adapter->pdev); if (adapter->have_msi) { handler = e1000_intr_msi; @@ -1131,6 +1131,10 @@ e1000_probe(struct pci_dev *pdev, /* initialize the wol settings based on the eeprom settings */ adapter->wol = adapter->eeprom_wol; + /* initialize our MSI capabilities */ + if (adapter->hw.mac_type >= e1000_82571) + adapter->have_msi = 1; + /* print bus type/speed/width info */ { struct e1000_hw *hw = &adapter->hw; @@ -1405,6 +1409,132 @@ e1000_alloc_queues(struct e1000_adapter *adapter) } /** + * 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); + + u32 icr = E1000_READ_REG(&adapter->hw, ICR); + DPRINTK(HW,INFO, "icr is %08X\n", icr); + if (icr & E1000_ICR_RXSEQ) { + adapter->have_msi = 1; + 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; + int err; + + /* poll_enable hasn't been called yet, so don't need disable */ + /* clear any pending events */ + E1000_READ_REG(&adapter->hw, 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; + } + + /* our temporary test variable */ + adapter->have_msi = 0; + wmb(); + + e1000_irq_enable(adapter); + + /* fire an unusual interrupt on the test handler */ + E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_RXSEQ); + E1000_WRITE_FLUSH(&adapter->hw); + msleep(50); + + e1000_irq_disable(adapter); + + rmb(); + if (!adapter->have_msi) { + err = -EIO; + DPRINTK(HW, 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 */ + DPRINTK(HW, INFO, "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 and 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->have_msi) + 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 */ + DPRINTK(PROBE, WARNING, "MSI interrupt test failed, using legacy " + "interrupt.\n"); + + e1000_free_irq(adapter); + adapter->have_msi = 0; + + err = e1000_request_irq(adapter); + + return err; +} + +/** * e1000_open - Called when a network interface is made active * @netdev: network interface device structure * @@ -1461,6 +1591,15 @@ e1000_open(struct net_device *netdev) if (err) goto err_req_irq; + /* work around PCIe errata with MSI interrupts causing some chipsets to + * ignore e1000 MSI messages, which means we need to test our MSI + * interrupt now */ + err = e1000_test_msi(adapter); + if (err) { + DPRINTK(PROBE, ERR, "Interrupt allocation failed\n"); + goto err_req_irq; + } + /* From here on the code is the same as e1000_up() */ clear_bit(__E1000_DOWN, &adapter->flags); -- 1.5.2.1