]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCIe: ASPM: Break out of endless loop waiting for PCI config bits to switch
authorThomas Renninger <trenn@suse.de>
Tue, 9 Dec 2008 12:05:09 +0000 (13:05 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Sat, 13 Dec 2008 23:29:32 +0000 (15:29 -0800)
commit 2a42d9dba7842422ffb2c02e75288a8bc2fd5065 upstream.

Makes a Compaq 6735s boot reliably again.  It used to hang in the loop
on some boots.  Give the link one second to train, otherwise break out
of the loop and reset the previously set clock bits.

Signed-off-by: Thomas Renninger <trenn@suse.de>
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Matthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/pci/pcie/aspm.c

index 851f5b83cdbc3f62f352627d0ee91848fc9923c0..aa6fda1ac55940f7860b7946b729f54e38c63469 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/jiffies.h>
 #include <linux/pci-aspm.h>
 #include "../pci.h"
 
@@ -161,11 +162,12 @@ static void pcie_check_clock_pm(struct pci_dev *pdev)
  */
 static void pcie_aspm_configure_common_clock(struct pci_dev *pdev)
 {
-       int pos, child_pos;
+       int pos, child_pos, i = 0;
        u16 reg16 = 0;
        struct pci_dev *child_dev;
        int same_clock = 1;
-
+       unsigned long start_jiffies;
+       u16 child_regs[8], parent_reg;
        /*
         * all functions of a slot should have the same Slot Clock
         * Configuration, so just check one function
@@ -191,16 +193,19 @@ static void pcie_aspm_configure_common_clock(struct pci_dev *pdev)
                child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP);
                pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKCTL,
                        &reg16);
+               child_regs[i] = reg16;
                if (same_clock)
                        reg16 |= PCI_EXP_LNKCTL_CCC;
                else
                        reg16 &= ~PCI_EXP_LNKCTL_CCC;
                pci_write_config_word(child_dev, child_pos + PCI_EXP_LNKCTL,
                        reg16);
+               i++;
        }
 
        /* Configure upstream component */
        pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
+       parent_reg = reg16;
        if (same_clock)
                reg16 |= PCI_EXP_LNKCTL_CCC;
        else
@@ -212,12 +217,30 @@ static void pcie_aspm_configure_common_clock(struct pci_dev *pdev)
        pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
 
        /* Wait for link training end */
-       while (1) {
+       /* break out after waiting for 1 second */
+       start_jiffies = jiffies;
+       while ((jiffies - start_jiffies) < HZ) {
                pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, &reg16);
                if (!(reg16 & PCI_EXP_LNKSTA_LT))
                        break;
                cpu_relax();
        }
+       /* training failed -> recover */
+       if ((jiffies - start_jiffies) >= HZ) {
+               dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure"
+                           " common clock\n");
+               i = 0;
+               list_for_each_entry(child_dev, &pdev->subordinate->devices,
+                                   bus_list) {
+                       child_pos = pci_find_capability(child_dev,
+                                                       PCI_CAP_ID_EXP);
+                       pci_write_config_word(child_dev,
+                                             child_pos + PCI_EXP_LNKCTL,
+                                             child_regs[i]);
+                       i++;
+               }
+               pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, parent_reg);
+       }
 }
 
 /*