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:
parent
0cf04597b4
commit
13129d9b61
1 changed files with 60 additions and 27 deletions
|
@ -64,8 +64,6 @@ static int debug = -1;
|
||||||
module_param(debug, int, 0);
|
module_param(debug, int, 0);
|
||||||
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
|
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[] = {
|
static const struct e1000_info *e1000_info_tbl[] = {
|
||||||
[board_82571] = &e1000_82571_info,
|
[board_82571] = &e1000_82571_info,
|
||||||
[board_82572] = &e1000_82572_info,
|
[board_82572] = &e1000_82572_info,
|
||||||
|
@ -6019,38 +6017,73 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
|
||||||
return 0;
|
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);
|
struct pci_dev *parent = pdev->bus->self;
|
||||||
}
|
u16 aspm_dis_mask = 0;
|
||||||
#else
|
u16 pdev_aspmc, parent_aspmc;
|
||||||
static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
|
|
||||||
{
|
|
||||||
u16 aspm_ctl = 0;
|
|
||||||
|
|
||||||
if (state & PCIE_LINK_STATE_L0S)
|
switch (state) {
|
||||||
aspm_ctl |= PCI_EXP_LNKCTL_ASPM_L0S;
|
case PCIE_LINK_STATE_L0S:
|
||||||
if (state & PCIE_LINK_STATE_L1)
|
case PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1:
|
||||||
aspm_ctl |= PCI_EXP_LNKCTL_ASPM_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.
|
/* Both device and parent should have the same ASPM setting.
|
||||||
* Disable ASPM in downstream component first and then upstream.
|
* 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)
|
if (parent)
|
||||||
pcie_capability_clear_word(pdev->bus->self, PCI_EXP_LNKCTL,
|
pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
|
||||||
aspm_ctl);
|
aspm_dis_mask);
|
||||||
}
|
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
Loading…
Add table
Reference in a new issue