e1000e: Disable L1 ASPM power savings for 82573 mobile variants
L1 ASPM link (pci-e link power savings) has significant benefits
(~1W savings when link is active) but unfortunately does not work
correctly on any of the chipsets that have 82573 on mobile platforms
which causes various nuisances:
- eeprom reads return garbage information leading to bad eeprom
checksums
- long ping times (up to 2 seconds)
- complete system hangs (freeze/lockup)
A lot of T60 owners have been plagued by this, but other mobile
solutions also suffer from these symptoms.
Disabling L1 ASPM before we activate the PCI-E link fixes all of
these issues at the cost of some power consumption.
Remove a workaround RDTR adjustment that is no longer needed with
this new one.
Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 9cc5a6b..5450ef8 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -3509,6 +3509,33 @@
return 0;
}
+static void e1000e_disable_l1aspm(struct pci_dev *pdev)
+{
+ int pos;
+ u32 cap;
+ u16 val;
+
+ /*
+ * 82573 workaround - disable L1 ASPM on mobile chipsets
+ *
+ * L1 ASPM on various mobile (ich7) chipsets do not behave properly
+ * resulting in lost data or garbage information on the pci-e link
+ * level. This could result in (false) bad EEPROM checksum errors,
+ * long ping times (up to 2s) or even a system freeze/hang.
+ *
+ * Unfortunately this feature saves about 1W power consumption when
+ * active.
+ */
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &cap);
+ pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &val);
+ if (val & 0x2) {
+ dev_warn(&pdev->dev, "Disabling L1 ASPM\n");
+ val &= ~0x2;
+ pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, val);
+ }
+}
+
#ifdef CONFIG_PM
static int e1000_resume(struct pci_dev *pdev)
{
@@ -3519,6 +3546,7 @@
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+ e1000e_disable_l1aspm(pdev);
err = pci_enable_device(pdev);
if (err) {
dev_err(&pdev->dev,
@@ -3619,6 +3647,7 @@
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ e1000e_disable_l1aspm(pdev);
if (pci_enable_device(pdev)) {
dev_err(&pdev->dev,
"Cannot re-enable PCI device after reset.\n");
@@ -3720,6 +3749,7 @@
u16 eeprom_data = 0;
u16 eeprom_apme_mask = E1000_EEPROM_APME;
+ e1000e_disable_l1aspm(pdev);
err = pci_enable_device(pdev);
if (err)
return err;