| .. | .. |
|---|
| 192 | 192 | link->clkpm_disable = blacklist ? 1 : 0; |
|---|
| 193 | 193 | } |
|---|
| 194 | 194 | |
|---|
| 195 | | -static bool pcie_retrain_link(struct pcie_link_state *link) |
|---|
| 195 | +static int pcie_wait_for_retrain(struct pci_dev *pdev) |
|---|
| 196 | 196 | { |
|---|
| 197 | | - struct pci_dev *parent = link->pdev; |
|---|
| 198 | 197 | unsigned long end_jiffies; |
|---|
| 199 | 198 | u16 reg16; |
|---|
| 199 | + |
|---|
| 200 | + /* Wait for Link Training to be cleared by hardware */ |
|---|
| 201 | + end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; |
|---|
| 202 | + do { |
|---|
| 203 | + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, ®16); |
|---|
| 204 | + if (!(reg16 & PCI_EXP_LNKSTA_LT)) |
|---|
| 205 | + return 0; |
|---|
| 206 | + msleep(1); |
|---|
| 207 | + } while (time_before(jiffies, end_jiffies)); |
|---|
| 208 | + |
|---|
| 209 | + return -ETIMEDOUT; |
|---|
| 210 | +} |
|---|
| 211 | + |
|---|
| 212 | +static int pcie_retrain_link(struct pcie_link_state *link) |
|---|
| 213 | +{ |
|---|
| 214 | + struct pci_dev *parent = link->pdev; |
|---|
| 215 | + int rc; |
|---|
| 216 | + u16 reg16; |
|---|
| 217 | + |
|---|
| 218 | + /* |
|---|
| 219 | + * Ensure the updated LNKCTL parameters are used during link |
|---|
| 220 | + * training by checking that there is no ongoing link training to |
|---|
| 221 | + * avoid LTSSM race as recommended in Implementation Note at the |
|---|
| 222 | + * end of PCIe r6.0.1 sec 7.5.3.7. |
|---|
| 223 | + */ |
|---|
| 224 | + rc = pcie_wait_for_retrain(parent); |
|---|
| 225 | + if (rc) |
|---|
| 226 | + return rc; |
|---|
| 200 | 227 | |
|---|
| 201 | 228 | pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); |
|---|
| 202 | 229 | reg16 |= PCI_EXP_LNKCTL_RL; |
|---|
| .. | .. |
|---|
| 211 | 238 | pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); |
|---|
| 212 | 239 | } |
|---|
| 213 | 240 | |
|---|
| 214 | | - /* Wait for link training end. Break out after waiting for timeout */ |
|---|
| 215 | | - end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; |
|---|
| 216 | | - do { |
|---|
| 217 | | - pcie_capability_read_word(parent, PCI_EXP_LNKSTA, ®16); |
|---|
| 218 | | - if (!(reg16 & PCI_EXP_LNKSTA_LT)) |
|---|
| 219 | | - break; |
|---|
| 220 | | - msleep(1); |
|---|
| 221 | | - } while (time_before(jiffies, end_jiffies)); |
|---|
| 222 | | - return !(reg16 & PCI_EXP_LNKSTA_LT); |
|---|
| 241 | + return pcie_wait_for_retrain(parent); |
|---|
| 223 | 242 | } |
|---|
| 224 | 243 | |
|---|
| 225 | 244 | /* |
|---|
| .. | .. |
|---|
| 230 | 249 | static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) |
|---|
| 231 | 250 | { |
|---|
| 232 | 251 | int same_clock = 1; |
|---|
| 233 | | - u16 reg16, parent_reg, child_reg[8]; |
|---|
| 252 | + u16 reg16, ccc, parent_old_ccc, child_old_ccc[8]; |
|---|
| 234 | 253 | struct pci_dev *child, *parent = link->pdev; |
|---|
| 235 | 254 | struct pci_bus *linkbus = parent->subordinate; |
|---|
| 236 | 255 | /* |
|---|
| .. | .. |
|---|
| 252 | 271 | |
|---|
| 253 | 272 | /* Port might be already in common clock mode */ |
|---|
| 254 | 273 | pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); |
|---|
| 274 | + parent_old_ccc = reg16 & PCI_EXP_LNKCTL_CCC; |
|---|
| 255 | 275 | if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) { |
|---|
| 256 | 276 | bool consistent = true; |
|---|
| 257 | 277 | |
|---|
| .. | .. |
|---|
| 268 | 288 | pci_info(parent, "ASPM: current common clock configuration is inconsistent, reconfiguring\n"); |
|---|
| 269 | 289 | } |
|---|
| 270 | 290 | |
|---|
| 291 | + ccc = same_clock ? PCI_EXP_LNKCTL_CCC : 0; |
|---|
| 271 | 292 | /* Configure downstream component, all functions */ |
|---|
| 272 | 293 | list_for_each_entry(child, &linkbus->devices, bus_list) { |
|---|
| 273 | 294 | pcie_capability_read_word(child, PCI_EXP_LNKCTL, ®16); |
|---|
| 274 | | - child_reg[PCI_FUNC(child->devfn)] = reg16; |
|---|
| 275 | | - if (same_clock) |
|---|
| 276 | | - reg16 |= PCI_EXP_LNKCTL_CCC; |
|---|
| 277 | | - else |
|---|
| 278 | | - reg16 &= ~PCI_EXP_LNKCTL_CCC; |
|---|
| 279 | | - pcie_capability_write_word(child, PCI_EXP_LNKCTL, reg16); |
|---|
| 295 | + child_old_ccc[PCI_FUNC(child->devfn)] = reg16 & PCI_EXP_LNKCTL_CCC; |
|---|
| 296 | + pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, |
|---|
| 297 | + PCI_EXP_LNKCTL_CCC, ccc); |
|---|
| 280 | 298 | } |
|---|
| 281 | 299 | |
|---|
| 282 | 300 | /* Configure upstream component */ |
|---|
| 283 | | - pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); |
|---|
| 284 | | - parent_reg = reg16; |
|---|
| 285 | | - if (same_clock) |
|---|
| 286 | | - reg16 |= PCI_EXP_LNKCTL_CCC; |
|---|
| 287 | | - else |
|---|
| 288 | | - reg16 &= ~PCI_EXP_LNKCTL_CCC; |
|---|
| 289 | | - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); |
|---|
| 301 | + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, |
|---|
| 302 | + PCI_EXP_LNKCTL_CCC, ccc); |
|---|
| 290 | 303 | |
|---|
| 291 | | - if (pcie_retrain_link(link)) |
|---|
| 292 | | - return; |
|---|
| 304 | + if (pcie_retrain_link(link)) { |
|---|
| 293 | 305 | |
|---|
| 294 | | - /* Training failed. Restore common clock configurations */ |
|---|
| 295 | | - pci_err(parent, "ASPM: Could not configure common clock\n"); |
|---|
| 296 | | - list_for_each_entry(child, &linkbus->devices, bus_list) |
|---|
| 297 | | - pcie_capability_write_word(child, PCI_EXP_LNKCTL, |
|---|
| 298 | | - child_reg[PCI_FUNC(child->devfn)]); |
|---|
| 299 | | - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg); |
|---|
| 306 | + /* Training failed. Restore common clock configurations */ |
|---|
| 307 | + pci_err(parent, "ASPM: Could not configure common clock\n"); |
|---|
| 308 | + list_for_each_entry(child, &linkbus->devices, bus_list) |
|---|
| 309 | + pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, |
|---|
| 310 | + PCI_EXP_LNKCTL_CCC, |
|---|
| 311 | + child_old_ccc[PCI_FUNC(child->devfn)]); |
|---|
| 312 | + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, |
|---|
| 313 | + PCI_EXP_LNKCTL_CCC, parent_old_ccc); |
|---|
| 314 | + } |
|---|
| 300 | 315 | } |
|---|
| 301 | 316 | |
|---|
| 302 | 317 | /* Convert L0s latency encoding to ns */ |
|---|
| .. | .. |
|---|
| 993 | 1008 | |
|---|
| 994 | 1009 | down_read(&pci_bus_sem); |
|---|
| 995 | 1010 | mutex_lock(&aspm_lock); |
|---|
| 996 | | - /* |
|---|
| 997 | | - * All PCIe functions are in one slot, remove one function will remove |
|---|
| 998 | | - * the whole slot, so just wait until we are the last function left. |
|---|
| 999 | | - */ |
|---|
| 1000 | | - if (!list_empty(&parent->subordinate->devices)) |
|---|
| 1001 | | - goto out; |
|---|
| 1002 | 1011 | |
|---|
| 1003 | 1012 | link = parent->link_state; |
|---|
| 1004 | 1013 | root = link->root; |
|---|
| 1005 | 1014 | parent_link = link->parent; |
|---|
| 1006 | 1015 | |
|---|
| 1007 | | - /* All functions are removed, so just disable ASPM for the link */ |
|---|
| 1016 | + /* |
|---|
| 1017 | + * link->downstream is a pointer to the pci_dev of function 0. If |
|---|
| 1018 | + * we remove that function, the pci_dev is about to be deallocated, |
|---|
| 1019 | + * so we can't use link->downstream again. Free the link state to |
|---|
| 1020 | + * avoid this. |
|---|
| 1021 | + * |
|---|
| 1022 | + * If we're removing a non-0 function, it's possible we could |
|---|
| 1023 | + * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends |
|---|
| 1024 | + * programming the same ASPM Control value for all functions of |
|---|
| 1025 | + * multi-function devices, so disable ASPM for all of them. |
|---|
| 1026 | + */ |
|---|
| 1008 | 1027 | pcie_config_aspm_link(link, 0); |
|---|
| 1009 | 1028 | list_del(&link->sibling); |
|---|
| 1010 | | - /* Clock PM is for endpoint device */ |
|---|
| 1011 | 1029 | free_link_state(link); |
|---|
| 1012 | 1030 | |
|---|
| 1013 | 1031 | /* Recheck latencies and configure upstream links */ |
|---|
| .. | .. |
|---|
| 1015 | 1033 | pcie_update_aspm_capable(root); |
|---|
| 1016 | 1034 | pcie_config_aspm_path(parent_link); |
|---|
| 1017 | 1035 | } |
|---|
| 1018 | | -out: |
|---|
| 1036 | + |
|---|
| 1019 | 1037 | mutex_unlock(&aspm_lock); |
|---|
| 1020 | 1038 | up_read(&pci_bus_sem); |
|---|
| 1021 | 1039 | } |
|---|