aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Allan <bruce.w.allan@intel.com>2010-04-26 23:33:04 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-27 13:18:26 -0400
commit6f461f6c7c961f0b1b73c0f27becf472a0ac606b (patch)
treeebf91b3e79734386ab63e79fef9d2429b6c81a09
parent61fac744dddb22d99c7b12250bc9bada7866df08 (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>
-rw-r--r--drivers/net/e1000e/82571.c20
-rw-r--r--drivers/net/e1000e/e1000.h5
-rw-r--r--drivers/net/e1000e/netdev.c70
3 files changed, 55 insertions, 40 deletions
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index 712ccc66ba25..90155552ea09 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -336,7 +336,6 @@ static s32 e1000_get_variants_82571(struct e1000_adapter *adapter)
336 struct e1000_hw *hw = &adapter->hw; 336 struct e1000_hw *hw = &adapter->hw;
337 static int global_quad_port_a; /* global port a indication */ 337 static int global_quad_port_a; /* global port a indication */
338 struct pci_dev *pdev = adapter->pdev; 338 struct pci_dev *pdev = adapter->pdev;
339 u16 eeprom_data = 0;
340 int is_port_b = er32(STATUS) & E1000_STATUS_FUNC_1; 339 int is_port_b = er32(STATUS) & E1000_STATUS_FUNC_1;
341 s32 rc; 340 s32 rc;
342 341
@@ -387,16 +386,15 @@ static s32 e1000_get_variants_82571(struct e1000_adapter *adapter)
387 if (pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD) 386 if (pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD)
388 adapter->flags &= ~FLAG_HAS_WOL; 387 adapter->flags &= ~FLAG_HAS_WOL;
389 break; 388 break;
390
391 case e1000_82573: 389 case e1000_82573:
390 case e1000_82574:
391 case e1000_82583:
392 /* Disable ASPM L0s due to hardware errata */
393 e1000e_disable_aspm(adapter->pdev, PCIE_LINK_STATE_L0S);
394
392 if (pdev->device == E1000_DEV_ID_82573L) { 395 if (pdev->device == E1000_DEV_ID_82573L) {
393 if (e1000_read_nvm(&adapter->hw, NVM_INIT_3GIO_3, 1, 396 adapter->flags |= FLAG_HAS_JUMBO_FRAMES;
394 &eeprom_data) < 0) 397 adapter->max_hw_frame_size = DEFAULT_JUMBO;
395 break;
396 if (!(eeprom_data & NVM_WORD1A_ASPM_MASK)) {
397 adapter->flags |= FLAG_HAS_JUMBO_FRAMES;
398 adapter->max_hw_frame_size = DEFAULT_JUMBO;
399 }
400 } 398 }
401 break; 399 break;
402 default: 400 default:
@@ -1792,6 +1790,7 @@ struct e1000_info e1000_82571_info = {
1792 | FLAG_RESET_OVERWRITES_LAA /* errata */ 1790 | FLAG_RESET_OVERWRITES_LAA /* errata */
1793 | FLAG_TARC_SPEED_MODE_BIT /* errata */ 1791 | FLAG_TARC_SPEED_MODE_BIT /* errata */
1794 | FLAG_APME_CHECK_PORT_B, 1792 | FLAG_APME_CHECK_PORT_B,
1793 .flags2 = FLAG2_DISABLE_ASPM_L1, /* errata 13 */
1795 .pba = 38, 1794 .pba = 38,
1796 .max_hw_frame_size = DEFAULT_JUMBO, 1795 .max_hw_frame_size = DEFAULT_JUMBO,
1797 .get_variants = e1000_get_variants_82571, 1796 .get_variants = e1000_get_variants_82571,
@@ -1809,6 +1808,7 @@ struct e1000_info e1000_82572_info = {
1809 | FLAG_RX_CSUM_ENABLED 1808 | FLAG_RX_CSUM_ENABLED
1810 | FLAG_HAS_CTRLEXT_ON_LOAD 1809 | FLAG_HAS_CTRLEXT_ON_LOAD
1811 | FLAG_TARC_SPEED_MODE_BIT, /* errata */ 1810 | FLAG_TARC_SPEED_MODE_BIT, /* errata */
1811 .flags2 = FLAG2_DISABLE_ASPM_L1, /* errata 13 */
1812 .pba = 38, 1812 .pba = 38,
1813 .max_hw_frame_size = DEFAULT_JUMBO, 1813 .max_hw_frame_size = DEFAULT_JUMBO,
1814 .get_variants = e1000_get_variants_82571, 1814 .get_variants = e1000_get_variants_82571,
@@ -1820,13 +1820,11 @@ struct e1000_info e1000_82572_info = {
1820struct e1000_info e1000_82573_info = { 1820struct e1000_info e1000_82573_info = {
1821 .mac = e1000_82573, 1821 .mac = e1000_82573,
1822 .flags = FLAG_HAS_HW_VLAN_FILTER 1822 .flags = FLAG_HAS_HW_VLAN_FILTER
1823 | FLAG_HAS_JUMBO_FRAMES
1824 | FLAG_HAS_WOL 1823 | FLAG_HAS_WOL
1825 | FLAG_APME_IN_CTRL3 1824 | FLAG_APME_IN_CTRL3
1826 | FLAG_RX_CSUM_ENABLED 1825 | FLAG_RX_CSUM_ENABLED
1827 | FLAG_HAS_SMART_POWER_DOWN 1826 | FLAG_HAS_SMART_POWER_DOWN
1828 | FLAG_HAS_AMT 1827 | FLAG_HAS_AMT
1829 | FLAG_HAS_ERT
1830 | FLAG_HAS_SWSM_ON_LOAD, 1828 | FLAG_HAS_SWSM_ON_LOAD,
1831 .pba = 20, 1829 .pba = 20,
1832 .max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN, 1830 .max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN,
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index 118bdf483593..ee32b9b27a9f 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -37,6 +37,7 @@
37#include <linux/io.h> 37#include <linux/io.h>
38#include <linux/netdevice.h> 38#include <linux/netdevice.h>
39#include <linux/pci.h> 39#include <linux/pci.h>
40#include <linux/pci-aspm.h>
40 41
41#include "hw.h" 42#include "hw.h"
42 43
@@ -374,7 +375,7 @@ struct e1000_adapter {
374struct e1000_info { 375struct e1000_info {
375 enum e1000_mac_type mac; 376 enum e1000_mac_type mac;
376 unsigned int flags; 377 unsigned int flags;
377 unsigned int flags2; 378 unsigned int flags2;
378 u32 pba; 379 u32 pba;
379 u32 max_hw_frame_size; 380 u32 max_hw_frame_size;
380 s32 (*get_variants)(struct e1000_adapter *); 381 s32 (*get_variants)(struct e1000_adapter *);
@@ -421,6 +422,7 @@ struct e1000_info {
421#define FLAG2_CRC_STRIPPING (1 << 0) 422#define FLAG2_CRC_STRIPPING (1 << 0)
422#define FLAG2_HAS_PHY_WAKEUP (1 << 1) 423#define FLAG2_HAS_PHY_WAKEUP (1 << 1)
423#define FLAG2_IS_DISCARDING (1 << 2) 424#define FLAG2_IS_DISCARDING (1 << 2)
425#define FLAG2_DISABLE_ASPM_L1 (1 << 3)
424 426
425#define E1000_RX_DESC_PS(R, i) \ 427#define E1000_RX_DESC_PS(R, i) \
426 (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) 428 (&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
@@ -461,6 +463,7 @@ extern void e1000e_update_stats(struct e1000_adapter *adapter);
461extern bool e1000e_has_link(struct e1000_adapter *adapter); 463extern bool e1000e_has_link(struct e1000_adapter *adapter);
462extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter); 464extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
463extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter); 465extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
466extern void e1000e_disable_aspm(struct pci_dev *pdev, u16 state);
464 467
465extern unsigned int copybreak; 468extern unsigned int copybreak;
466 469
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
4608static void e1000e_disable_l1aspm(struct pci_dev *pdev) 4616#ifdef CONFIG_PCIEASPM
4617static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
4618{
4619 pci_disable_link_state(pdev, state);
4620}
4621#else
4622static 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, &reg16);
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, &reg16);
4638 reg16 &= ~state;
4639 pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16);
4640}
4641#endif
4642void 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
4901static const struct net_device_ops e1000e_netdev_ops = { 4914static 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)