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