diff options
Diffstat (limited to 'drivers/net/e1000e/netdev.c')
| -rw-r--r-- | drivers/net/e1000e/netdev.c | 87 | 
1 files changed, 49 insertions, 38 deletions
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 88d54d3efce..dbf81788bb4 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -36,6 +36,7 @@  #include <linux/netdevice.h>  #include <linux/tcp.h>  #include <linux/ipv6.h> +#include <linux/slab.h>  #include <net/checksum.h>  #include <net/ip6_checksum.h>  #include <linux/mii.h> @@ -660,6 +661,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter)  				i = 0;  		} +		if (i == tx_ring->next_to_use) +			break;  		eop = tx_ring->buffer_info[i].next_to_watch;  		eop_desc = E1000_TX_DESC(*tx_ring, eop);  	} @@ -2289,8 +2292,6 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)  	ew32(TCTL, tctl);  	e1000e_config_collision_dist(hw); - -	adapter->tx_queue_len = adapter->netdev->tx_queue_len;  }  /** @@ -2877,7 +2878,6 @@ void e1000e_down(struct e1000_adapter *adapter)  	del_timer_sync(&adapter->watchdog_timer);  	del_timer_sync(&adapter->phy_info_timer); -	netdev->tx_queue_len = adapter->tx_queue_len;  	netif_carrier_off(netdev);  	adapter->link_speed = 0;  	adapter->link_duplex = 0; @@ -3588,21 +3588,15 @@ static void e1000_watchdog_task(struct work_struct *work)  					       "link gets many collisions.\n");  			} -			/* -			 * tweak tx_queue_len according to speed/duplex -			 * and adjust the timeout factor -			 */ -			netdev->tx_queue_len = adapter->tx_queue_len; +			/* adjust timeout factor according to speed/duplex */  			adapter->tx_timeout_factor = 1;  			switch (adapter->link_speed) {  			case SPEED_10:  				txb2b = 0; -				netdev->tx_queue_len = 10;  				adapter->tx_timeout_factor = 16;  				break;  			case SPEED_100:  				txb2b = 0; -				netdev->tx_queue_len = 100;  				adapter->tx_timeout_factor = 10;  				break;  			} @@ -4289,6 +4283,14 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)  		return -EINVAL;  	} +	/* 82573 Errata 17 */ +	if (((adapter->hw.mac.type == e1000_82573) || +	     (adapter->hw.mac.type == e1000_82574)) && +	    (max_frame > ETH_FRAME_LEN + ETH_FCS_LEN)) { +		adapter->flags2 |= FLAG2_DISABLE_ASPM_L1; +		e1000e_disable_aspm(adapter->pdev, PCIE_LINK_STATE_L1); +	} +  	while (test_and_set_bit(__E1000_RESETTING, &adapter->state))  		msleep(1);  	/* e1000e_down -> e1000e_reset dependent on max_frame_size & mtu */ @@ -4611,29 +4613,42 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,  	}  } -static void e1000e_disable_l1aspm(struct pci_dev *pdev) +#ifdef CONFIG_PCIEASPM +static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) +{ +	pci_disable_link_state(pdev, state); +} +#else +static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)  {  	int pos; -	u16 val; +	u16 reg16;  	/* -	 * 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. +	 * Both device and parent should have the same ASPM setting. +	 * Disable ASPM in downstream component first and then upstream.  	 */ -	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); -	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); -	} +	pos = pci_pcie_cap(pdev); +	pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); +	reg16 &= ~state; +	pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); + +	if (!pdev->bus->self) +		return; + +	pos = pci_pcie_cap(pdev->bus->self); +	pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, ®16); +	reg16 &= ~state; +	pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16); +} +#endif +void e1000e_disable_aspm(struct pci_dev *pdev, u16 state) +{ +	dev_info(&pdev->dev, "Disabling ASPM %s %s\n", +		 (state & PCIE_LINK_STATE_L0S) ? "L0s" : "", +		 (state & PCIE_LINK_STATE_L1) ? "L1" : ""); + +	__e1000e_disable_aspm(pdev, state);  }  #ifdef CONFIG_PM @@ -4659,7 +4674,8 @@ static int e1000_resume(struct pci_dev *pdev)  	pci_set_power_state(pdev, PCI_D0);  	pci_restore_state(pdev);  	pci_save_state(pdev); -	e1000e_disable_l1aspm(pdev); +	if (adapter->flags2 & FLAG2_DISABLE_ASPM_L1) +		e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1);  	err = pci_enable_device_mem(pdev);  	if (err) { @@ -4801,7 +4817,8 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)  	int err;  	pci_ers_result_t result; -	e1000e_disable_l1aspm(pdev); +	if (adapter->flags2 & FLAG2_DISABLE_ASPM_L1) +		e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1);  	err = pci_enable_device_mem(pdev);  	if (err) {  		dev_err(&pdev->dev, @@ -4895,13 +4912,6 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter)  		dev_warn(&adapter->pdev->dev,  			 "Warning: detected DSPD enabled in EEPROM\n");  	} - -	ret_val = e1000_read_nvm(hw, NVM_INIT_3GIO_3, 1, &buf); -	if (!ret_val && (le16_to_cpu(buf) & (3 << 2))) { -		/* ASPM enable */ -		dev_warn(&adapter->pdev->dev, -			 "Warning: detected ASPM enabled in EEPROM\n"); -	}  }  static const struct net_device_ops e1000e_netdev_ops = { @@ -4950,7 +4960,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,  	u16 eeprom_data = 0;  	u16 eeprom_apme_mask = E1000_EEPROM_APME; -	e1000e_disable_l1aspm(pdev); +	if (ei->flags2 & FLAG2_DISABLE_ASPM_L1) +		e1000e_disable_aspm(pdev, PCIE_LINK_STATE_L1);  	err = pci_enable_device_mem(pdev);  	if (err)  |