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>

authored by Thomas Renninger and committed by Jesse Barnes 2a42d9db 3b5dd45e

+26 -3
+26 -3
drivers/pci/pcie/aspm.c
··· 16 16 #include <linux/pm.h> 17 17 #include <linux/init.h> 18 18 #include <linux/slab.h> 19 + #include <linux/jiffies.h> 19 20 #include <linux/pci-aspm.h> 20 21 #include "../pci.h" 21 22 ··· 162 161 */ 163 162 static void pcie_aspm_configure_common_clock(struct pci_dev *pdev) 164 163 { 165 - int pos, child_pos; 164 + int pos, child_pos, i = 0; 166 165 u16 reg16 = 0; 167 166 struct pci_dev *child_dev; 168 167 int same_clock = 1; 169 - 168 + unsigned long start_jiffies; 169 + u16 child_regs[8], parent_reg; 170 170 /* 171 171 * all functions of a slot should have the same Slot Clock 172 172 * Configuration, so just check one function ··· 193 191 child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); 194 192 pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, 195 193 &reg16); 194 + child_regs[i] = reg16; 196 195 if (same_clock) 197 196 reg16 |= PCI_EXP_LNKCTL_CCC; 198 197 else 199 198 reg16 &= ~PCI_EXP_LNKCTL_CCC; 200 199 pci_write_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, 201 200 reg16); 201 + i++; 202 202 } 203 203 204 204 /* Configure upstream component */ 205 205 pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16); 206 + parent_reg = reg16; 206 207 if (same_clock) 207 208 reg16 |= PCI_EXP_LNKCTL_CCC; 208 209 else ··· 217 212 pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); 218 213 219 214 /* Wait for link training end */ 220 - while (1) { 215 + /* break out after waiting for 1 second */ 216 + start_jiffies = jiffies; 217 + while ((jiffies - start_jiffies) < HZ) { 221 218 pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, &reg16); 222 219 if (!(reg16 & PCI_EXP_LNKSTA_LT)) 223 220 break; 224 221 cpu_relax(); 222 + } 223 + /* training failed -> recover */ 224 + if ((jiffies - start_jiffies) >= HZ) { 225 + dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure" 226 + " common clock\n"); 227 + i = 0; 228 + list_for_each_entry(child_dev, &pdev->subordinate->devices, 229 + bus_list) { 230 + child_pos = pci_find_capability(child_dev, 231 + PCI_CAP_ID_EXP); 232 + pci_write_config_word(child_dev, 233 + child_pos + PCI_EXP_LNKCTL, 234 + child_regs[i]); 235 + i++; 236 + } 237 + pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, parent_reg); 225 238 } 226 239 } 227 240