hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/drivers/pci/pcie/aspm.c
....@@ -192,11 +192,38 @@
192192 link->clkpm_disable = blacklist ? 1 : 0;
193193 }
194194
195
-static bool pcie_retrain_link(struct pcie_link_state *link)
195
+static int pcie_wait_for_retrain(struct pci_dev *pdev)
196196 {
197
- struct pci_dev *parent = link->pdev;
198197 unsigned long end_jiffies;
199198 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, &reg16);
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;
200227
201228 pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
202229 reg16 |= PCI_EXP_LNKCTL_RL;
....@@ -211,15 +238,7 @@
211238 pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
212239 }
213240
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, &reg16);
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);
223242 }
224243
225244 /*
....@@ -230,7 +249,7 @@
230249 static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
231250 {
232251 int same_clock = 1;
233
- u16 reg16, parent_reg, child_reg[8];
252
+ u16 reg16, ccc, parent_old_ccc, child_old_ccc[8];
234253 struct pci_dev *child, *parent = link->pdev;
235254 struct pci_bus *linkbus = parent->subordinate;
236255 /*
....@@ -252,6 +271,7 @@
252271
253272 /* Port might be already in common clock mode */
254273 pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
274
+ parent_old_ccc = reg16 & PCI_EXP_LNKCTL_CCC;
255275 if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) {
256276 bool consistent = true;
257277
....@@ -268,35 +288,30 @@
268288 pci_info(parent, "ASPM: current common clock configuration is inconsistent, reconfiguring\n");
269289 }
270290
291
+ ccc = same_clock ? PCI_EXP_LNKCTL_CCC : 0;
271292 /* Configure downstream component, all functions */
272293 list_for_each_entry(child, &linkbus->devices, bus_list) {
273294 pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
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);
280298 }
281299
282300 /* Configure upstream component */
283
- pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
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);
290303
291
- if (pcie_retrain_link(link))
292
- return;
304
+ if (pcie_retrain_link(link)) {
293305
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
+ }
300315 }
301316
302317 /* Convert L0s latency encoding to ns */
....@@ -993,21 +1008,24 @@
9931008
9941009 down_read(&pci_bus_sem);
9951010 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;
10021011
10031012 link = parent->link_state;
10041013 root = link->root;
10051014 parent_link = link->parent;
10061015
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
+ */
10081027 pcie_config_aspm_link(link, 0);
10091028 list_del(&link->sibling);
1010
- /* Clock PM is for endpoint device */
10111029 free_link_state(link);
10121030
10131031 /* Recheck latencies and configure upstream links */
....@@ -1015,7 +1033,7 @@
10151033 pcie_update_aspm_capable(root);
10161034 pcie_config_aspm_path(parent_link);
10171035 }
1018
-out:
1036
+
10191037 mutex_unlock(&aspm_lock);
10201038 up_read(&pci_bus_sem);
10211039 }