.. | .. |
---|
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 | } |
---|