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:
parent
3b5dd45e94
commit
2a42d9dba7
1 changed files with 26 additions and 3 deletions
|
@ -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,
|
||||
®16);
|
||||
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, ®16);
|
||||
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, ®16);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue