1 // SPDX-License-Identifier: GPL-2.0
3 * TPH (TLP Processing Hints) support
5 * Copyright (C) 2024 Advanced Micro Devices, Inc.
6 * Eric Van Tassell <Eric.VanTassell@amd.com>
7 * Wei Huang <wei.huang2@amd.com>
10 #include <linux/pci-acpi.h>
11 #include <linux/msi.h>
12 #include <linux/bitfield.h>
13 #include <linux/pci-tph.h>
17 /* System-wide TPH disabled */
18 static bool pci_tph_disabled
;
22 * The st_info struct defines the Steering Tag (ST) info returned by the
23 * firmware PCI ACPI _DSM method (rev=0x7, func=0xF, "_DSM to Query Cache
24 * Locality TPH Features"), as specified in the approved ECN for PCI Firmware
25 * Spec and available at https://members.pcisig.com/wg/PCI-SIG/document/15470.
27 * @vm_st_valid: 8-bit ST for volatile memory is valid
28 * @vm_xst_valid: 16-bit extended ST for volatile memory is valid
29 * @vm_ph_ignore: 1 => PH was and will be ignored, 0 => PH should be supplied
30 * @vm_st: 8-bit ST for volatile mem
31 * @vm_xst: 16-bit extended ST for volatile mem
32 * @pm_st_valid: 8-bit ST for persistent memory is valid
33 * @pm_xst_valid: 16-bit extended ST for persistent memory is valid
34 * @pm_ph_ignore: 1 => PH was and will be ignored, 0 => PH should be supplied
35 * @pm_st: 8-bit ST for persistent mem
36 * @pm_xst: 16-bit extended ST for persistent mem
56 static u16
tph_extract_tag(enum tph_mem_type mem_type
, u8 req_type
,
60 case PCI_TPH_REQ_TPH_ONLY
: /* 8-bit tag */
63 if (info
->vm_st_valid
)
67 if (info
->pm_st_valid
)
72 case PCI_TPH_REQ_EXT_TPH
: /* 16-bit tag */
75 if (info
->vm_xst_valid
)
79 if (info
->pm_xst_valid
)
91 #define TPH_ST_DSM_FUNC_INDEX 0xF
92 static acpi_status
tph_invoke_dsm(acpi_handle handle
, u32 cpu_uid
,
93 union st_info
*st_out
)
95 union acpi_object arg3
[3], in_obj
, *out_obj
;
97 if (!acpi_check_dsm(handle
, &pci_acpi_dsm_guid
, 7,
98 BIT(TPH_ST_DSM_FUNC_INDEX
)))
101 /* DWORD: feature ID (0 for processor cache ST query) */
102 arg3
[0].integer
.type
= ACPI_TYPE_INTEGER
;
103 arg3
[0].integer
.value
= 0;
105 /* DWORD: target UID */
106 arg3
[1].integer
.type
= ACPI_TYPE_INTEGER
;
107 arg3
[1].integer
.value
= cpu_uid
;
109 /* QWORD: properties, all 0's */
110 arg3
[2].integer
.type
= ACPI_TYPE_INTEGER
;
111 arg3
[2].integer
.value
= 0;
113 in_obj
.type
= ACPI_TYPE_PACKAGE
;
114 in_obj
.package
.count
= ARRAY_SIZE(arg3
);
115 in_obj
.package
.elements
= arg3
;
117 out_obj
= acpi_evaluate_dsm(handle
, &pci_acpi_dsm_guid
, 7,
118 TPH_ST_DSM_FUNC_INDEX
, &in_obj
);
122 if (out_obj
->type
!= ACPI_TYPE_BUFFER
) {
127 st_out
->value
= *((u64
*)(out_obj
->buffer
.pointer
));
135 /* Update the TPH Requester Enable field of TPH Control Register */
136 static void set_ctrl_reg_req_en(struct pci_dev
*pdev
, u8 req_type
)
140 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, ®
);
142 reg
&= ~PCI_TPH_CTRL_REQ_EN_MASK
;
143 reg
|= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK
, req_type
);
145 pci_write_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, reg
);
148 static u8
get_st_modes(struct pci_dev
*pdev
)
152 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CAP
, ®
);
153 reg
&= PCI_TPH_CAP_ST_NS
| PCI_TPH_CAP_ST_IV
| PCI_TPH_CAP_ST_DS
;
158 static u32
get_st_table_loc(struct pci_dev
*pdev
)
162 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CAP
, ®
);
164 return FIELD_GET(PCI_TPH_CAP_LOC_MASK
, reg
);
168 * Return the size of ST table. If ST table is not in TPH Requester Extended
169 * Capability space, return 0. Otherwise return the ST Table Size + 1.
171 static u16
get_st_table_size(struct pci_dev
*pdev
)
176 /* Check ST table location first */
177 loc
= get_st_table_loc(pdev
);
179 /* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */
180 loc
= FIELD_PREP(PCI_TPH_CAP_LOC_MASK
, loc
);
181 if (loc
!= PCI_TPH_LOC_CAP
)
184 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CAP
, ®
);
186 return FIELD_GET(PCI_TPH_CAP_ST_MASK
, reg
) + 1;
189 /* Return device's Root Port completer capability */
190 static u8
get_rp_completer_type(struct pci_dev
*pdev
)
196 rp
= pcie_find_root_port(pdev
);
200 ret
= pcie_capability_read_dword(rp
, PCI_EXP_DEVCAP2
, ®
);
204 return FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK
, reg
);
207 /* Write ST to MSI-X vector control reg - Return 0 if OK, otherwise -errno */
208 static int write_tag_to_msix(struct pci_dev
*pdev
, int msix_idx
, u16 tag
)
210 #ifdef CONFIG_PCI_MSI
211 struct msi_desc
*msi_desc
= NULL
;
212 void __iomem
*vec_ctrl
;
216 msi_lock_descs(&pdev
->dev
);
218 /* Find the msi_desc entry with matching msix_idx */
219 msi_for_each_desc(msi_desc
, &pdev
->dev
, MSI_DESC_ASSOCIATED
) {
220 if (msi_desc
->msi_index
== msix_idx
)
229 /* Get the vector control register (offset 0xc) pointed by msix_idx */
230 vec_ctrl
= pdev
->msix_base
+ msix_idx
* PCI_MSIX_ENTRY_SIZE
;
231 vec_ctrl
+= PCI_MSIX_ENTRY_VECTOR_CTRL
;
233 val
= readl(vec_ctrl
);
234 val
&= ~PCI_MSIX_ENTRY_CTRL_ST
;
235 val
|= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST
, tag
);
236 writel(val
, vec_ctrl
);
238 /* Read back to flush the update */
239 val
= readl(vec_ctrl
);
242 msi_unlock_descs(&pdev
->dev
);
249 /* Write tag to ST table - Return 0 if OK, otherwise -errno */
250 static int write_tag_to_st_table(struct pci_dev
*pdev
, int index
, u16 tag
)
255 /* Check if index is out of bound */
256 st_table_size
= get_st_table_size(pdev
);
257 if (index
>= st_table_size
)
260 offset
= pdev
->tph_cap
+ PCI_TPH_BASE_SIZEOF
+ index
* sizeof(u16
);
262 return pci_write_config_word(pdev
, offset
, tag
);
266 * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated
267 * with a specific CPU
269 * @mem_type: target memory type (volatile or persistent RAM)
270 * @cpu_uid: associated CPU id
271 * @tag: Steering Tag to be returned
273 * Return the Steering Tag for a target memory that is associated with a
274 * specific CPU as indicated by cpu_uid.
276 * Return: 0 if success, otherwise negative value (-errno)
278 int pcie_tph_get_cpu_st(struct pci_dev
*pdev
, enum tph_mem_type mem_type
,
279 unsigned int cpu_uid
, u16
*tag
)
283 acpi_handle rp_acpi_handle
;
286 rp
= pcie_find_root_port(pdev
);
287 if (!rp
|| !rp
->bus
|| !rp
->bus
->bridge
)
290 rp_acpi_handle
= ACPI_HANDLE(rp
->bus
->bridge
);
292 if (tph_invoke_dsm(rp_acpi_handle
, cpu_uid
, &info
) != AE_OK
) {
297 *tag
= tph_extract_tag(mem_type
, pdev
->tph_req_type
, &info
);
299 pci_dbg(pdev
, "get steering tag: mem_type=%s, cpu_uid=%d, tag=%#04x\n",
300 (mem_type
== TPH_MEM_TYPE_VM
) ? "volatile" : "persistent",
308 EXPORT_SYMBOL(pcie_tph_get_cpu_st
);
311 * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry
313 * @index: ST table entry index
314 * @tag: Steering Tag to be written
316 * Figure out the proper location of ST table, either in the MSI-X table or
317 * in the TPH Extended Capability space, and write the Steering Tag into
318 * the ST entry pointed by index.
320 * Return: 0 if success, otherwise negative value (-errno)
322 int pcie_tph_set_st_entry(struct pci_dev
*pdev
, unsigned int index
, u16 tag
)
330 if (!pdev
->tph_enabled
)
333 /* No need to write tag if device is in "No ST Mode" */
334 if (pdev
->tph_mode
== PCI_TPH_ST_NS_MODE
)
338 * Disable TPH before updating ST to avoid potential instability as
339 * cautioned in PCIe r6.2, sec 6.17.3, "ST Modes of Operation"
341 set_ctrl_reg_req_en(pdev
, PCI_TPH_REQ_DISABLE
);
343 loc
= get_st_table_loc(pdev
);
344 /* Convert loc to match with PCI_TPH_LOC_* */
345 loc
= FIELD_PREP(PCI_TPH_CAP_LOC_MASK
, loc
);
348 case PCI_TPH_LOC_MSIX
:
349 err
= write_tag_to_msix(pdev
, index
, tag
);
351 case PCI_TPH_LOC_CAP
:
352 err
= write_tag_to_st_table(pdev
, index
, tag
);
359 pcie_disable_tph(pdev
);
363 set_ctrl_reg_req_en(pdev
, pdev
->tph_mode
);
365 pci_dbg(pdev
, "set steering tag: %s table, index=%d, tag=%#04x\n",
366 (loc
== PCI_TPH_LOC_MSIX
) ? "MSI-X" : "ST", index
, tag
);
370 EXPORT_SYMBOL(pcie_tph_set_st_entry
);
373 * pcie_disable_tph - Turn off TPH support for device
378 void pcie_disable_tph(struct pci_dev
*pdev
)
383 if (!pdev
->tph_enabled
)
386 pci_write_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, 0);
389 pdev
->tph_req_type
= 0;
390 pdev
->tph_enabled
= 0;
392 EXPORT_SYMBOL(pcie_disable_tph
);
395 * pcie_enable_tph - Enable TPH support for device using a specific ST mode
397 * @mode: ST mode to enable. Current supported modes include:
399 * - PCI_TPH_ST_NS_MODE: NO ST Mode
400 * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode
401 * - PCI_TPH_ST_DS_MODE: Device Specific Mode
403 * Check whether the mode is actually supported by the device before enabling
404 * and return an error if not. Additionally determine what types of requests,
405 * TPH or extended TPH, can be issued by the device based on its TPH requester
406 * capability and the Root Port's completer capability.
408 * Return: 0 on success, otherwise negative value (-errno)
410 int pcie_enable_tph(struct pci_dev
*pdev
, int mode
)
416 /* Honor "notph" kernel parameter */
417 if (pci_tph_disabled
)
423 if (pdev
->tph_enabled
)
426 /* Sanitize and check ST mode compatibility */
427 mode
&= PCI_TPH_CTRL_MODE_SEL_MASK
;
428 dev_modes
= get_st_modes(pdev
);
429 if (!((1 << mode
) & dev_modes
))
432 pdev
->tph_mode
= mode
;
434 /* Get req_type supported by device and its Root Port */
435 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CAP
, ®
);
436 if (FIELD_GET(PCI_TPH_CAP_EXT_TPH
, reg
))
437 pdev
->tph_req_type
= PCI_TPH_REQ_EXT_TPH
;
439 pdev
->tph_req_type
= PCI_TPH_REQ_TPH_ONLY
;
441 rp_req_type
= get_rp_completer_type(pdev
);
443 /* Final req_type is the smallest value of two */
444 pdev
->tph_req_type
= min(pdev
->tph_req_type
, rp_req_type
);
446 if (pdev
->tph_req_type
== PCI_TPH_REQ_DISABLE
)
449 /* Write them into TPH control register */
450 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, ®
);
452 reg
&= ~PCI_TPH_CTRL_MODE_SEL_MASK
;
453 reg
|= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK
, pdev
->tph_mode
);
455 reg
&= ~PCI_TPH_CTRL_REQ_EN_MASK
;
456 reg
|= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK
, pdev
->tph_req_type
);
458 pci_write_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, reg
);
460 pdev
->tph_enabled
= 1;
464 EXPORT_SYMBOL(pcie_enable_tph
);
466 void pci_restore_tph_state(struct pci_dev
*pdev
)
468 struct pci_cap_saved_state
*save_state
;
469 int num_entries
, i
, offset
;
476 if (!pdev
->tph_enabled
)
479 save_state
= pci_find_saved_ext_cap(pdev
, PCI_EXT_CAP_ID_TPH
);
483 /* Restore control register and all ST entries */
484 cap
= &save_state
->cap
.data
[0];
485 pci_write_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, *cap
++);
486 st_entry
= (u16
*)cap
;
487 offset
= PCI_TPH_BASE_SIZEOF
;
488 num_entries
= get_st_table_size(pdev
);
489 for (i
= 0; i
< num_entries
; i
++) {
490 pci_write_config_word(pdev
, pdev
->tph_cap
+ offset
,
492 offset
+= sizeof(u16
);
496 void pci_save_tph_state(struct pci_dev
*pdev
)
498 struct pci_cap_saved_state
*save_state
;
499 int num_entries
, i
, offset
;
506 if (!pdev
->tph_enabled
)
509 save_state
= pci_find_saved_ext_cap(pdev
, PCI_EXT_CAP_ID_TPH
);
513 /* Save control register */
514 cap
= &save_state
->cap
.data
[0];
515 pci_read_config_dword(pdev
, pdev
->tph_cap
+ PCI_TPH_CTRL
, cap
++);
517 /* Save all ST entries in extended capability structure */
518 st_entry
= (u16
*)cap
;
519 offset
= PCI_TPH_BASE_SIZEOF
;
520 num_entries
= get_st_table_size(pdev
);
521 for (i
= 0; i
< num_entries
; i
++) {
522 pci_read_config_word(pdev
, pdev
->tph_cap
+ offset
,
524 offset
+= sizeof(u16
);
528 void pci_no_tph(void)
530 pci_tph_disabled
= true;
532 pr_info("PCIe TPH is disabled\n");
535 void pci_tph_init(struct pci_dev
*pdev
)
540 pdev
->tph_cap
= pci_find_ext_capability(pdev
, PCI_EXT_CAP_ID_TPH
);
544 num_entries
= get_st_table_size(pdev
);
545 save_size
= sizeof(u32
) + num_entries
* sizeof(u16
);
546 pci_add_ext_cap_save_buffer(pdev
, PCI_EXT_CAP_ID_TPH
, save_size
);