|
@@ -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)
|
|
|
{
|
|
|
+ struct pci_dev *parent = pdev->bus->self;
|
|
|
+ u16 aspm_dis_mask = 0;
|
|
|
+ u16 pdev_aspmc, parent_aspmc;
|
|
|
+
|
|
|
+ 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);
|
|
|
-}
|
|
|
-#else
|
|
|
-static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
|
|
|
-{
|
|
|
- u16 aspm_ctl = 0;
|
|
|
|
|
|
- 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;
|
|
|
+ /* 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);
|
|
|
-
|
|
|
- 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" : "");
|
|
|
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, aspm_dis_mask);
|
|
|
|
|
|
- __e1000e_disable_aspm(pdev, state);
|
|
|
+ if (parent)
|
|
|
+ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
|
|
|
+ aspm_dis_mask);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|