diff mbox series

[V2,6/9] PCI/TPH: Retrieve steering tag from ACPI _DSM

Message ID 20240531213841.3246055-7-wei.huang2@amd.com (mailing list archive)
State Not Applicable
Headers show
Series PCIe TPH and cache direct injection support | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 908 this patch: 908
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 906 this patch: 906
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 912 this patch: 912
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 155 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Wei Huang May 31, 2024, 9:38 p.m. UTC
According to PCI SIG ECN, calling the _DSM firmware method for a given
CPU_UID returns the steering tags for different types of memory
(volatile, non-volatile). These tags are supposed to be used in ST
table entry for optimal results.

Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com>
Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
Signed-off-by: Wei Huang <wei.huang2@amd.com>
Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> 
Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
---
 drivers/pci/pcie/tph.c  | 103 +++++++++++++++++++++++++++++++++++++++-
 include/linux/pci-tph.h |  34 +++++++++++++
 2 files changed, 136 insertions(+), 1 deletion(-)

Comments

Simon Horman June 4, 2024, 3:30 p.m. UTC | #1
On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote:
> According to PCI SIG ECN, calling the _DSM firmware method for a given
> CPU_UID returns the steering tags for different types of memory
> (volatile, non-volatile). These tags are supposed to be used in ST
> table entry for optimal results.
> 
> Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com>
> Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
> Signed-off-by: Wei Huang <wei.huang2@amd.com>
> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> 
> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>

...

> diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c

...

> +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph,
> +		       u8 target_type, bool cache_ref_valid,
> +		       u64 cache_ref, union st_info *st_out)
> +{
> +	union acpi_object in_obj, in_buf[3], *out_obj;
> +
> +	in_buf[0].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */
> +
> +	in_buf[1].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[1].integer.value = cpu_uid;
> +
> +	in_buf[2].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[2].integer.value = ph & 3;
> +	in_buf[2].integer.value |= (target_type & 1) << 2;
> +	in_buf[2].integer.value |= (cache_ref_valid & 1) << 3;
> +	in_buf[2].integer.value |= (cache_ref << 32);
> +
> +	in_obj.type = ACPI_TYPE_PACKAGE;
> +	in_obj.package.count = ARRAY_SIZE(in_buf);
> +	in_obj.package.elements = in_buf;
> +
> +	out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
> +				    ST_DSM_FUNC_INDEX, &in_obj);

Hi Wei Huang, Eric, all,

This seems to break builds on ARM (32bit) with multi_v7_defconfig.

  .../tph.c:221:39: error: use of undeclared identifier 'pci_acpi_dsm_guid'
  221 |         out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
      |

I suspect a dependency on ACPI in Kconfig is appropriate.

> +
> +	if (!out_obj)
> +		return false;
> +
> +	if (out_obj->type != ACPI_TYPE_BUFFER) {
> +		pr_err("invalid return type %d from TPH _DSM\n",
> +		       out_obj->type);
> +		ACPI_FREE(out_obj);
> +		return false;
> +	}

...
Wei Huang June 5, 2024, 7:34 p.m. UTC | #2
On 6/4/24 10:30, Simon Horman wrote:
> On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote:
>> According to PCI SIG ECN, calling the _DSM firmware method for a given
>> CPU_UID returns the steering tags for different types of memory
>> (volatile, non-volatile). These tags are supposed to be used in ST
>> table entry for optimal results.
>>
>> Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com>
>> Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
>> Signed-off-by: Wei Huang <wei.huang2@amd.com>
>> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
>> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> 
>> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
> 
> ...
> 
>> diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c
> 
> ...
> 
>> +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph,
>> +		       u8 target_type, bool cache_ref_valid,
>> +		       u64 cache_ref, union st_info *st_out)
>> +{
>> +	union acpi_object in_obj, in_buf[3], *out_obj;
>> +
>> +	in_buf[0].integer.type = ACPI_TYPE_INTEGER;
>> +	in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */
>> +
>> +	in_buf[1].integer.type = ACPI_TYPE_INTEGER;
>> +	in_buf[1].integer.value = cpu_uid;
>> +
>> +	in_buf[2].integer.type = ACPI_TYPE_INTEGER;
>> +	in_buf[2].integer.value = ph & 3;
>> +	in_buf[2].integer.value |= (target_type & 1) << 2;
>> +	in_buf[2].integer.value |= (cache_ref_valid & 1) << 3;
>> +	in_buf[2].integer.value |= (cache_ref << 32);
>> +
>> +	in_obj.type = ACPI_TYPE_PACKAGE;
>> +	in_obj.package.count = ARRAY_SIZE(in_buf);
>> +	in_obj.package.elements = in_buf;
>> +
>> +	out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
>> +				    ST_DSM_FUNC_INDEX, &in_obj);
> 
> Hi Wei Huang, Eric, all,
> 
> This seems to break builds on ARM (32bit) with multi_v7_defconfig.
> 
>   .../tph.c:221:39: error: use of undeclared identifier 'pci_acpi_dsm_guid'
>   221 |         out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
>       |

Thanks for pointing it out. I will add "depends on ACPI" in Kconfig
which solves the problem:

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnu- multi_v7_defconfig zImage
modules
  CALL    scripts/checksyscalls.sh
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/zImage is ready

> 
> I suspect a dependency on ACPI in Kconfig is appropriate.
> 
>> +
>> +	if (!out_obj)
>> +		return false;
>> +
>> +	if (out_obj->type != ACPI_TYPE_BUFFER) {
>> +		pr_err("invalid return type %d from TPH _DSM\n",
>> +		       out_obj->type);
>> +		ACPI_FREE(out_obj);
>> +		return false;
>> +	}
> 
> ...
Jonathan Cameron June 7, 2024, 5:39 p.m. UTC | #3
On Fri, 31 May 2024 16:38:38 -0500
Wei Huang <wei.huang2@amd.com> wrote:

> According to PCI SIG ECN, calling the _DSM firmware method for a given
> CPU_UID returns the steering tags for different types of memory
> (volatile, non-volatile). These tags are supposed to be used in ST
> table entry for optimal results.
> 
> Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com>
> Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
> Signed-off-by: Wei Huang <wei.huang2@amd.com>
> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> 
> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
Took a very quick look at this only due to lack of time..

> ---
>  drivers/pci/pcie/tph.c  | 103 +++++++++++++++++++++++++++++++++++++++-
>  include/linux/pci-tph.h |  34 +++++++++++++
>  2 files changed, 136 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c
> index 320b99c60365..425935a14b62 100644
> --- a/drivers/pci/pcie/tph.c
> +++ b/drivers/pci/pcie/tph.c
> @@ -158,6 +158,98 @@ static int tph_get_table_location(struct pci_dev *dev, u8 *loc_out)
>  	return 0;
>  }
>  
> +static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type,
> +			   union st_info *st_tag)
> +{
> +	switch (req_type) {
> +	case PCI_TPH_REQ_TPH_ONLY: /* 8 bit tags */
> +		switch (mem_type) {
> +		case TPH_MEM_TYPE_VM:
> +			if (st_tag->vm_st_valid)
> +				return st_tag->vm_st;
> +			break;
> +		case TPH_MEM_TYPE_PM:
> +			if (st_tag->pm_st_valid)
> +				return st_tag->pm_st;
> +			break;
> +		}
> +		break;
> +	case PCI_TPH_REQ_EXT_TPH: /* 16 bit tags */
> +		switch (mem_type) {
> +		case TPH_MEM_TYPE_VM:
> +			if (st_tag->vm_xst_valid)
> +				return st_tag->vm_xst;
> +			break;
> +		case TPH_MEM_TYPE_PM:
> +			if (st_tag->pm_xst_valid)
> +				return st_tag->pm_xst;
> +			break;
> +		}
> +		break;
> +	default:
> +		pr_err("invalid steering tag in ACPI _DSM\n");
> +		return 0;
Not an error code?  If so need to explain why 0 is the right thing to
return.
> +	}
> +
> +	return 0;
> +}
> +
> +#define MIN_ST_DSM_REV		7
> +#define ST_DSM_FUNC_INDEX	0xf
> +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph,

give that a pci / tph prefix of some type as it's a very generic
name so potential future name clashes likely.

> +		       u8 target_type, bool cache_ref_valid,
> +		       u64 cache_ref, union st_info *st_out)
> +{
> +	union acpi_object in_obj, in_buf[3], *out_obj;

I'm out of time for the day, so not checked this. Will look more
closely in v3.

> +
> +	in_buf[0].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */
> +
> +	in_buf[1].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[1].integer.value = cpu_uid;
> +
> +	in_buf[2].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[2].integer.value = ph & 3;
> +	in_buf[2].integer.value |= (target_type & 1) << 2;
> +	in_buf[2].integer.value |= (cache_ref_valid & 1) << 3;
> +	in_buf[2].integer.value |= (cache_ref << 32);
> +
> +	in_obj.type = ACPI_TYPE_PACKAGE;
> +	in_obj.package.count = ARRAY_SIZE(in_buf);
> +	in_obj.package.elements = in_buf;
> +
> +	out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
> +				    ST_DSM_FUNC_INDEX, &in_obj);
> +
> +	if (!out_obj)
> +		return false;
> +
> +	if (out_obj->type != ACPI_TYPE_BUFFER) {
> +		pr_err("invalid return type %d from TPH _DSM\n",
> +		       out_obj->type);
> +		ACPI_FREE(out_obj);
> +		return false;
> +	}
> +
> +	st_out->value = *((u64 *)(out_obj->buffer.pointer));
> +
> +	ACPI_FREE(out_obj);
> +
> +	return true;
> +}
> +

>  static bool msix_nr_in_bounds(struct pci_dev *dev, int msix_nr)
>  {
>  	u16 tbl_sz;
> @@ -441,7 +533,16 @@ bool pcie_tph_get_st(struct pci_dev *dev, unsigned int cpu,
>  		    enum tph_mem_type mem_type, u8 req_type,
>  		    u16 *tag)
Add this function in this patch as it's not used before here and all
the logic is about the _DSM
Note name needs to change though.

>  {
> -	*tag = 0;
> +	union st_info info;
> +
> +	if (!invoke_dsm(root_complex_acpi_handle(dev), cpu, 0, 0, false, 0,
> +			&info)) {
> +		*tag = 0;
> +		return false;
> +	}
> +
> +	*tag = tph_extract_tag(mem_type, req_type, &info);
> +	pr_debug("%s: cpu=%d tag=%d\n", __func__, cpu, *tag);
>  
>  	return true;
>  }
> diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
> index 4fbd1e2fd98c..79533c6254c2 100644
> --- a/include/linux/pci-tph.h
> +++ b/include/linux/pci-tph.h
> @@ -14,6 +14,40 @@ enum tph_mem_type {
>  	TPH_MEM_TYPE_PM		/* persistent memory type */
>  };
>  
> +/*
> + * The st_info struct defines the steering tag returned by the firmware _DSM
> + * method defined in PCI SIG ECN. The specification is available at:
> + * https://members.pcisig.com/wg/PCI-SIG/document/15470.
> +
> + * @vm_st_valid:  8 bit tag for volatile memory is valid
> + * @vm_xst_valid: 16 bit tag for volatile memory is valid
> + * @vm_ignore:    1 => was and will be ignored, 0 => ph should be supplied
> + * @vm_st:        8 bit steering tag for volatile mem
> + * @vm_xst:       16 bit steering tag for volatile mem
> + * @pm_st_valid:  8 bit tag for persistent memory is valid
> + * @pm_xst_valid: 16 bit tag for persistent memory is valid
> + * @pm_ignore:    1 => was and will be ignore, 0 => ph should be supplied
pm_ph_ignore

> + * @pm_st:        8 bit steering tag for persistent mem
> + * @pm_xst:       16 bit steering tag for persistent mem
> + */
> +union st_info {
> +	struct {
> +		u64 vm_st_valid:1,
> +		vm_xst_valid:1,
> +		vm_ph_ignore:1,
> +		rsvd1:5,
> +		vm_st:8,
> +		vm_xst:16,
> +		pm_st_valid:1,
> +		pm_xst_valid:1,
> +		pm_ph_ignore:1,
> +		rsvd2:5,
> +		pm_st:8,
> +		pm_xst:16;
> +	};
> +	u64 value;
> +};
Firstly why in a header? If it did want to be then pci-acpi.h might be reasonable.

> +
>  #ifdef CONFIG_PCIE_TPH
>  int pcie_tph_disable(struct pci_dev *dev);
>  int tph_set_dev_nostmode(struct pci_dev *dev);
Bjorn Helgaas June 7, 2024, 6:43 p.m. UTC | #4
On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote:
> According to PCI SIG ECN, calling the _DSM firmware method for a given
> CPU_UID returns the steering tags for different types of memory
> (volatile, non-volatile). These tags are supposed to be used in ST
> table entry for optimal results.

Cite PCI Firmware spec if possible.  If it hasn't been incorporated
yet, at least include the exact name of the ECN and the date it was
approved.

Say what the patch does in the commit log (in addition to the subject
line).

> +#define MIN_ST_DSM_REV		7

No useful value in this #define.  If the value ever changes, code
changes will be required too.

> +#define ST_DSM_FUNC_INDEX	0xf

Move to the list in pci-acpi.h with name similar to others.

> +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph,
> +		       u8 target_type, bool cache_ref_valid,
> +		       u64 cache_ref, union st_info *st_out)
> +{

Return 0 or -errno.  "invoke_dsm" is not a predicate with an obvious
true/false meaning.

> +	union acpi_object in_obj, in_buf[3], *out_obj;
> +
> +	in_buf[0].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */
> +
> +	in_buf[1].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[1].integer.value = cpu_uid;
> +
> +	in_buf[2].integer.type = ACPI_TYPE_INTEGER;
> +	in_buf[2].integer.value = ph & 3;
> +	in_buf[2].integer.value |= (target_type & 1) << 2;
> +	in_buf[2].integer.value |= (cache_ref_valid & 1) << 3;
> +	in_buf[2].integer.value |= (cache_ref << 32);
> +
> +	in_obj.type = ACPI_TYPE_PACKAGE;
> +	in_obj.package.count = ARRAY_SIZE(in_buf);
> +	in_obj.package.elements = in_buf;

Must check whether this _DSM function is implemented first, e.g., see
acpi_enable_dpc().

> +	out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
> +				    ST_DSM_FUNC_INDEX, &in_obj);
> +
> +	if (!out_obj)
> +		return false;
> +
> +	if (out_obj->type != ACPI_TYPE_BUFFER) {
> +		pr_err("invalid return type %d from TPH _DSM\n",
> +		       out_obj->type);
> +		ACPI_FREE(out_obj);
> +		return false;
> +	}
> +
> +	st_out->value = *((u64 *)(out_obj->buffer.pointer));
> +
> +	ACPI_FREE(out_obj);
> +
> +	return true;
> +}
diff mbox series

Patch

diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c
index 320b99c60365..425935a14b62 100644
--- a/drivers/pci/pcie/tph.c
+++ b/drivers/pci/pcie/tph.c
@@ -158,6 +158,98 @@  static int tph_get_table_location(struct pci_dev *dev, u8 *loc_out)
 	return 0;
 }
 
+static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type,
+			   union st_info *st_tag)
+{
+	switch (req_type) {
+	case PCI_TPH_REQ_TPH_ONLY: /* 8 bit tags */
+		switch (mem_type) {
+		case TPH_MEM_TYPE_VM:
+			if (st_tag->vm_st_valid)
+				return st_tag->vm_st;
+			break;
+		case TPH_MEM_TYPE_PM:
+			if (st_tag->pm_st_valid)
+				return st_tag->pm_st;
+			break;
+		}
+		break;
+	case PCI_TPH_REQ_EXT_TPH: /* 16 bit tags */
+		switch (mem_type) {
+		case TPH_MEM_TYPE_VM:
+			if (st_tag->vm_xst_valid)
+				return st_tag->vm_xst;
+			break;
+		case TPH_MEM_TYPE_PM:
+			if (st_tag->pm_xst_valid)
+				return st_tag->pm_xst;
+			break;
+		}
+		break;
+	default:
+		pr_err("invalid steering tag in ACPI _DSM\n");
+		return 0;
+	}
+
+	return 0;
+}
+
+#define MIN_ST_DSM_REV		7
+#define ST_DSM_FUNC_INDEX	0xf
+static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph,
+		       u8 target_type, bool cache_ref_valid,
+		       u64 cache_ref, union st_info *st_out)
+{
+	union acpi_object in_obj, in_buf[3], *out_obj;
+
+	in_buf[0].integer.type = ACPI_TYPE_INTEGER;
+	in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */
+
+	in_buf[1].integer.type = ACPI_TYPE_INTEGER;
+	in_buf[1].integer.value = cpu_uid;
+
+	in_buf[2].integer.type = ACPI_TYPE_INTEGER;
+	in_buf[2].integer.value = ph & 3;
+	in_buf[2].integer.value |= (target_type & 1) << 2;
+	in_buf[2].integer.value |= (cache_ref_valid & 1) << 3;
+	in_buf[2].integer.value |= (cache_ref << 32);
+
+	in_obj.type = ACPI_TYPE_PACKAGE;
+	in_obj.package.count = ARRAY_SIZE(in_buf);
+	in_obj.package.elements = in_buf;
+
+	out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV,
+				    ST_DSM_FUNC_INDEX, &in_obj);
+
+	if (!out_obj)
+		return false;
+
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		pr_err("invalid return type %d from TPH _DSM\n",
+		       out_obj->type);
+		ACPI_FREE(out_obj);
+		return false;
+	}
+
+	st_out->value = *((u64 *)(out_obj->buffer.pointer));
+
+	ACPI_FREE(out_obj);
+
+	return true;
+}
+
+static acpi_handle root_complex_acpi_handle(struct pci_dev *dev)
+{
+	struct pci_dev *root_port;
+
+	root_port = pcie_find_root_port(dev);
+
+	if (!root_port || !root_port->bus || !root_port->bus->bridge)
+		return NULL;
+
+	return ACPI_HANDLE(root_port->bus->bridge);
+}
+
 static bool msix_nr_in_bounds(struct pci_dev *dev, int msix_nr)
 {
 	u16 tbl_sz;
@@ -441,7 +533,16 @@  bool pcie_tph_get_st(struct pci_dev *dev, unsigned int cpu,
 		    enum tph_mem_type mem_type, u8 req_type,
 		    u16 *tag)
 {
-	*tag = 0;
+	union st_info info;
+
+	if (!invoke_dsm(root_complex_acpi_handle(dev), cpu, 0, 0, false, 0,
+			&info)) {
+		*tag = 0;
+		return false;
+	}
+
+	*tag = tph_extract_tag(mem_type, req_type, &info);
+	pr_debug("%s: cpu=%d tag=%d\n", __func__, cpu, *tag);
 
 	return true;
 }
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
index 4fbd1e2fd98c..79533c6254c2 100644
--- a/include/linux/pci-tph.h
+++ b/include/linux/pci-tph.h
@@ -14,6 +14,40 @@  enum tph_mem_type {
 	TPH_MEM_TYPE_PM		/* persistent memory type */
 };
 
+/*
+ * The st_info struct defines the steering tag returned by the firmware _DSM
+ * method defined in PCI SIG ECN. The specification is available at:
+ * https://members.pcisig.com/wg/PCI-SIG/document/15470.
+
+ * @vm_st_valid:  8 bit tag for volatile memory is valid
+ * @vm_xst_valid: 16 bit tag for volatile memory is valid
+ * @vm_ignore:    1 => was and will be ignored, 0 => ph should be supplied
+ * @vm_st:        8 bit steering tag for volatile mem
+ * @vm_xst:       16 bit steering tag for volatile mem
+ * @pm_st_valid:  8 bit tag for persistent memory is valid
+ * @pm_xst_valid: 16 bit tag for persistent memory is valid
+ * @pm_ignore:    1 => was and will be ignore, 0 => ph should be supplied
+ * @pm_st:        8 bit steering tag for persistent mem
+ * @pm_xst:       16 bit steering tag for persistent mem
+ */
+union st_info {
+	struct {
+		u64 vm_st_valid:1,
+		vm_xst_valid:1,
+		vm_ph_ignore:1,
+		rsvd1:5,
+		vm_st:8,
+		vm_xst:16,
+		pm_st_valid:1,
+		pm_xst_valid:1,
+		pm_ph_ignore:1,
+		rsvd2:5,
+		pm_st:8,
+		pm_xst:16;
+	};
+	u64 value;
+};
+
 #ifdef CONFIG_PCIE_TPH
 int pcie_tph_disable(struct pci_dev *dev);
 int tph_set_dev_nostmode(struct pci_dev *dev);