diff options
author | Bruce Allan <bruce.w.allan@intel.com> | 2010-04-26 23:33:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-27 13:18:26 -0400 |
commit | 6f461f6c7c961f0b1b73c0f27becf472a0ac606b (patch) | |
tree | ebf91b3e79734386ab63e79fef9d2429b6c81a09 /drivers/net/e1000e/netdev.c | |
parent | 61fac744dddb22d99c7b12250bc9bada7866df08 (diff) |
e1000e: enable/disable ASPM L0s and L1 and ERT according to hardware errata
Prompted by a previous patch submitted by Matthew Garret <mjg@redhat.com>,
further digging into errata documentation reveals the current enabling or
disabling of ASPM L0s and L1 states for certain parts supported by this
driver are incorrect. 82571 and 82572 should always disable L1. For
standard frames, 82573/82574/82583 can enable L1 but L0s must be disabled,
and for jumbo frames 82573/82574 must disable L1. This allows for some
parts to enable L1 in certain configurations leading to better power
savings.
Also according to the same errata, Early Receive (ERT) should be disabled
on 82573 when using jumbo frames.
Cc: Matthew Garret <mjg@redhat.com>
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/e1000e/netdev.c')
-rw-r--r-- | drivers/net/e1000e/netdev.c | 70 |
1 files changed, 42 insertions, 28 deletions
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 73d43c53015a..fb8fc7d1b50d 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c | |||
@@ -4283,6 +4283,14 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) | |||
4283 | return -EINVAL; | 4283 | return -EINVAL; |
4284 | } | 4284 | } |
4285 | 4285 | ||
4286 | /* 82573 Errata 17 */ | ||
4287 | if (((adapter->hw.mac.type == e1000_82573) || | ||
4288 | (adapter->hw.mac.type == e1000_82574)) && | ||
4289 | (max_frame > ETH_FRAME_LEN + ETH_FCS_LEN)) { | ||
4290 | adapter->flags2 |= FLAG2_DISABLE_ASPM_L1; | ||
4291 | e1000e_disable_aspm(adapter->pdev, PCIE_LINK_STATE_L1); | ||
4292 | } | ||
4293 | |||
4286 | while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) | 4294 | while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) |
4287 | msleep(1); | 4295 | msleep(1); |
4288 | /* e1000e_down -> e1000e_reset dependent on max_frame_size & mtu */ | 4296 | /* e1000e_down -> e1000e_reset dependent on max_frame_size & mtu */ |
@@ -4605,29 +4613,39 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep, | |||
4605 | } | 4613 | } |
4606 | } | 4614 | } |
4607 | 4615 | ||
4608 | static void e1000e_disable_l1aspm(struct pci_dev *pdev) | 4616 | #ifdef CONFIG_PCIEASPM |
4617 | static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) | ||
4618 | { | ||
4619 | pci_disable_link_state(pdev, state); | ||
4620 | } | ||
4621 | #else | ||
4622 | static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) | ||
4609 | { | 4623 | { |
4610 | int pos; | 4624 | int pos; |
4611 | u16 val; | 4625 | u16 reg16; |
4612 | 4626 | ||
4613 | /* | 4627 | /* |
4614 | * 82573 workaround - disable L1 ASPM on mobile chipsets | 4628 | * Both device and parent should have the same ASPM setting. |
4615 | * | 4629 | * Disable ASPM in downstream component first and then upstream. |
4616 | * L1 ASPM on various mobile (ich7) chipsets do not behave properly | ||
4617 | * resulting in lost data or garbage information on the pci-e link | ||
4618 | * level. This could result in (false) bad EEPROM checksum errors, | ||
4619 | * long ping times (up to 2s) or even a system freeze/hang. | ||
4620 | * | ||
4621 | * Unfortunately this feature saves about 1W power consumption when | ||
4622 | * active. | ||
4623 | */ | 4630 | */ |
4624 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); | 4631 | pos = pci_pcie_cap(pdev); |
4625 | pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &val); | 4632 | pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); |
4626 | if (val & 0x2) { | 4633 | reg16 &= ~state; |
4627 | dev_warn(&pdev->dev, "Disabling L1 ASPM\n"); | 4634 | pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); |
4628 | val &= ~0x2; | 4635 | |
4629 | pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, val); | 4636 | pos = pci_pcie_cap(pdev->bus->self); |
4630 | } | 4637 | pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, ®16); |
4638 | reg16 &= ~state; | ||
4639 | pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16); | ||
4640 | } | ||
4641 | #endif | ||
4642 | void e1000e_disable_aspm(struct pci_dev *pdev, u16 state) | ||
4643 | { | ||
4644 | dev_info(&pdev->dev, "Disabling ASPM %s %s\n", | ||
4645 | (state & PCIE_LINK_STATE_L0S) ? "L0s" : "", | ||
4646 | (state & PCIE_LINK_STATE_L1) ? "L1" : ""); | ||
4647 | |||
4648 | __e1000e_disable_aspm(pdev, state); | ||
4631 | } | 4649 | } |
4632 | 4650 | ||
4633 | #ifdef CONFIG_PM | 4651 | #ifdef CONFIG_PM |
@@ -4653,7 +4671,8 @@ static int e1000_resume(struct pci_dev *pdev) | |||
4653 | pci_set_power_state(pdev, PCI_D0); | 4671 | pci_set_power_state(pdev, PCI_D0); |
4654 | pci_restore_state(pdev); | 4672 | pci_restore_state(pdev); |
4655 | pci_save_state(pdev); | 4673 | pci_save_state(pdev); |
4656 | e1000e_disable_l1aspm(pdev); | 4674 | if (adapter->flags2 & FLAG2_DISABLE_ASPM_L1) |
4675 | e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1); | ||
4657 | 4676 | ||
4658 | err = pci_enable_device_mem(pdev); | 4677 | err = pci_enable_device_mem(pdev); |
4659 | if (err) { | 4678 | if (err) { |
@@ -4795,7 +4814,8 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev) | |||
4795 | int err; | 4814 | int err; |
4796 | pci_ers_result_t result; | 4815 | pci_ers_result_t result; |
4797 | 4816 | ||
4798 | e1000e_disable_l1aspm(pdev); | 4817 | if (adapter->flags2 & FLAG2_DISABLE_ASPM_L1) |
4818 | e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1); | ||
4799 | err = pci_enable_device_mem(pdev); | 4819 | err = pci_enable_device_mem(pdev); |
4800 | if (err) { | 4820 | if (err) { |
4801 | dev_err(&pdev->dev, | 4821 | dev_err(&pdev->dev, |
@@ -4889,13 +4909,6 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter) | |||
4889 | dev_warn(&adapter->pdev->dev, | 4909 | dev_warn(&adapter->pdev->dev, |
4890 | "Warning: detected DSPD enabled in EEPROM\n"); | 4910 | "Warning: detected DSPD enabled in EEPROM\n"); |
4891 | } | 4911 | } |
4892 | |||
4893 | ret_val = e1000_read_nvm(hw, NVM_INIT_3GIO_3, 1, &buf); | ||
4894 | if (!ret_val && (le16_to_cpu(buf) & (3 << 2))) { | ||
4895 | /* ASPM enable */ | ||
4896 | dev_warn(&adapter->pdev->dev, | ||
4897 | "Warning: detected ASPM enabled in EEPROM\n"); | ||
4898 | } | ||
4899 | } | 4912 | } |
4900 | 4913 | ||
4901 | static const struct net_device_ops e1000e_netdev_ops = { | 4914 | static const struct net_device_ops e1000e_netdev_ops = { |
@@ -4944,7 +4957,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev, | |||
4944 | u16 eeprom_data = 0; | 4957 | u16 eeprom_data = 0; |
4945 | u16 eeprom_apme_mask = E1000_EEPROM_APME; | 4958 | u16 eeprom_apme_mask = E1000_EEPROM_APME; |
4946 | 4959 | ||
4947 | e1000e_disable_l1aspm(pdev); | 4960 | if (ei->flags2 & FLAG2_DISABLE_ASPM_L1) |
4961 | e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1); | ||
4948 | 4962 | ||
4949 | err = pci_enable_device_mem(pdev); | 4963 | err = pci_enable_device_mem(pdev); |
4950 | if (err) | 4964 | if (err) |