e1000e: Add code to check for failure of pci_disable_link_state call

This patch attempts to work around a problem found with some systems where
the call to pci_diable_link_state_locked() fails.  As a result, ASPM is not,
in fact, disabled.  Changing disable ASPM code to check if state actually
is disabled after the call and, if not, try another way to disable it.

Signed-off-by: Carolyn Wyborny <carolyn.wyborny@intel.com>
Acked-by: Bruce W. Allan <bruce.w.allan@intel.com>
Tested-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Carolyn Wyborny 2013-08-03 01:53:54 +00:00 committed by Jeff Kirsher
parent 0cf04597b4
commit 13129d9b61
1 changed files with 60 additions and 27 deletions

View File

@ -64,8 +64,6 @@ static int debug = -1;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state);
static const struct e1000_info *e1000_info_tbl[] = {
[board_82571] = &e1000_82571_info,
[board_82572] = &e1000_82572_info,
@ -6019,38 +6017,73 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
return 0;
}
#ifdef CONFIG_PCIEASPM
static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
/**
* e1000e_disable_aspm - Disable ASPM states
* @pdev: pointer to PCI device struct
* @state: bit-mask of ASPM states to disable
*
* Some devices *must* have certain ASPM states disabled per hardware errata.
**/
static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
{
pci_disable_link_state_locked(pdev, state);
}
#else
static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
{
u16 aspm_ctl = 0;
struct pci_dev *parent = pdev->bus->self;
u16 aspm_dis_mask = 0;
u16 pdev_aspmc, parent_aspmc;
if (state & PCIE_LINK_STATE_L0S)
aspm_ctl |= PCI_EXP_LNKCTL_ASPM_L0S;
if (state & PCIE_LINK_STATE_L1)
aspm_ctl |= PCI_EXP_LNKCTL_ASPM_L1;
switch (state) {
case PCIE_LINK_STATE_L0S:
case PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1:
aspm_dis_mask |= PCI_EXP_LNKCTL_ASPM_L0S;
/* fall-through - can't have L1 without L0s */
case PCIE_LINK_STATE_L1:
aspm_dis_mask |= PCI_EXP_LNKCTL_ASPM_L1;
break;
default:
return;
}
pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc);
pdev_aspmc &= PCI_EXP_LNKCTL_ASPMC;
if (parent) {
pcie_capability_read_word(parent, PCI_EXP_LNKCTL,
&parent_aspmc);
parent_aspmc &= PCI_EXP_LNKCTL_ASPMC;
}
/* Nothing to do if the ASPM states to be disabled already are */
if (!(pdev_aspmc & aspm_dis_mask) &&
(!parent || !(parent_aspmc & aspm_dis_mask)))
return;
dev_info(&pdev->dev, "Disabling ASPM %s %s\n",
(aspm_dis_mask & pdev_aspmc & PCI_EXP_LNKCTL_ASPM_L0S) ?
"L0s" : "",
(aspm_dis_mask & pdev_aspmc & PCI_EXP_LNKCTL_ASPM_L1) ?
"L1" : "");
#ifdef CONFIG_PCIEASPM
pci_disable_link_state_locked(pdev, state);
/* Double-check ASPM control. If not disabled by the above, the
* BIOS is preventing that from happening (or CONFIG_PCIEASPM is
* not enabled); override by writing PCI config space directly.
*/
pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc);
pdev_aspmc &= PCI_EXP_LNKCTL_ASPMC;
if (!(aspm_dis_mask & pdev_aspmc))
return;
#endif
/* Both device and parent should have the same ASPM setting.
* Disable ASPM in downstream component first and then upstream.
*/
pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, aspm_ctl);
pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, aspm_dis_mask);
if (pdev->bus->self)
pcie_capability_clear_word(pdev->bus->self, PCI_EXP_LNKCTL,
aspm_ctl);
}
#endif
static 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);
if (parent)
pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
aspm_dis_mask);
}
#ifdef CONFIG_PM