e1000e: Add code to check for failure of pci_disable_link_state call
authorCarolyn Wyborny <carolyn.wyborny@intel.com>
Sat, 3 Aug 2013 01:53:54 +0000 (01:53 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 22 Aug 2013 09:33:15 +0000 (02:33 -0700)
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>
drivers/net/ethernet/intel/e1000e/netdev.c

index e6d2c0f8f76a8cf7f32fcbb9f0e7f8f126064bf4..59c0c54137979ad119ec2363cfd5d53f66ad5a31 100644 (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)
 {
+       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