PCIe: ASPM: Break out of endless loop waiting for PCI config bits to switch

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.

Cc: stable@vger.kernel.org
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>
This commit is contained in:
Thomas Renninger 2008-12-09 13:05:09 +01:00 committed by Jesse Barnes
parent 3b5dd45e94
commit 2a42d9dba7
1 changed files with 26 additions and 3 deletions

View File

@ -16,6 +16,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/pci-aspm.h> #include <linux/pci-aspm.h>
#include "../pci.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) static void pcie_aspm_configure_common_clock(struct pci_dev *pdev)
{ {
int pos, child_pos; int pos, child_pos, i = 0;
u16 reg16 = 0; u16 reg16 = 0;
struct pci_dev *child_dev; struct pci_dev *child_dev;
int same_clock = 1; 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 * all functions of a slot should have the same Slot Clock
* Configuration, so just check one function * 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); child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP);
pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKCTL,
&reg16); &reg16);
child_regs[i] = reg16;
if (same_clock) if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC; reg16 |= PCI_EXP_LNKCTL_CCC;
else else
reg16 &= ~PCI_EXP_LNKCTL_CCC; reg16 &= ~PCI_EXP_LNKCTL_CCC;
pci_write_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, pci_write_config_word(child_dev, child_pos + PCI_EXP_LNKCTL,
reg16); reg16);
i++;
} }
/* Configure upstream component */ /* Configure upstream component */
pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16); pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
parent_reg = reg16;
if (same_clock) if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC; reg16 |= PCI_EXP_LNKCTL_CCC;
else 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); pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
/* Wait for link training end */ /* 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); pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_LT)) if (!(reg16 & PCI_EXP_LNKSTA_LT))
break; break;
cpu_relax(); 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);
}
} }
/* /*