feat(lib/pcie): add dvsec helpers

Add DVSEC RME DA support and helpers based on RME System
Architecture [1].

[1] https://developer.arm.com/documentation/den0129/latest

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I29c2dc3c94fa295c9948f63f57f88e2763326291
diff --git a/include/lib/pcie/pcie_dvsec_rmeda.h b/include/lib/pcie/pcie_dvsec_rmeda.h
new file mode 100644
index 0000000..b693f2a
--- /dev/null
+++ b/include/lib/pcie/pcie_dvsec_rmeda.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PCIE_DVSEC_RME_DA_H
+#define PCIE_DVSEC_RME_DA_H
+
+/* PCI RootPort Extended Capability RMEDA registers offset */
+
+/*
+ * Extended Capability Header
+ * DVSEC Headers
+ * RME-DA Control registers
+ */
+#define PCIE_ECAP_ECH_OFFSET			U(0)
+#define PCIE_ECAP_DVSEC_HDR1_OFFSET		U(4)
+#define PCIE_ECAP_DVSEC_HDR2_OFFSET		U(8)
+#define PCIE_ECAP_DVSEC_RME_DA_CTL_REG1_OFFSET	U(12)
+#define PCIE_ECAP_DVSEC_RME_DA_CTL_REG2_OFFSET	U(16)
+
+/* RME-DA DVSEC Header1 */
+#define DVSEC_HDR1_VENDOR_ID_SHIFT		U(0)
+#define DVSEC_HDR1_VENDOR_ID_WIDTH		U(16)
+#define DVSEC_HDR1_REVISION_SHIFT		U(16)
+#define DVSEC_HDR1_REVISION_WIDTH		U(4)
+#define DVSEC_HDR1_LENGTH_SHIFT			U(20)
+#define DVSEC_HDR1_LENGTH_WIDTH			U(12)
+
+/* RME-DA DVSEC Header1 - Values */
+#define DVSEC_VENDOR_ID_ARM			U(0x13b5)
+#define DVSEC_REVISION_0			U(0x0)
+
+/* RME-DA DVSEC Header2 */
+#define DVSEC_HDR2_DVSEC_ID_SHIFT		U(0)
+#define DVSEC_HDR2_DVSEC_ID_WIDTH		U(16)
+
+/* RME-DA DVSEC Header2 - Values */
+#define DVSEC_ID_RME_DA				U(0xFF01)
+
+/* RME-DA Control register 1 */
+#define DVSEC_RMEDA_CTL_REG1_TDISP_EN_SHIFT	U(0)
+#define DVSEC_RMEDA_CTL_REG1_TDISP_EN_WIDTH	U(1)
+
+/* RME-DA Control register 1 - Values */
+#define RME_DA_TDISP_DISABLE			U(0)
+#define RME_DA_TDISP_ENABLE			U(1)
+
+/* RME-DA Control register 2. 32 IDE Selective Stream Lock bits */
+#define DVSEC_RME_DA_CTL_REG2_SEL_STR_LOCK_SHIFT	U(0)
+#define DVSEC_RME_DA_CTL_REG2_SEL_STR_LOCK_WIDTH	U(32)
+
+uint32_t pcie_find_rmeda_capability(uint32_t bdf, uint32_t *cid_offset);
+
+#endif /* PCIE_DVSEC_RME_DA_H */
diff --git a/include/lib/pcie/pcie_spec.h b/include/lib/pcie/pcie_spec.h
index 31fd98b..cd851ee 100644
--- a/include/lib/pcie/pcie_spec.h
+++ b/include/lib/pcie/pcie_spec.h
@@ -87,6 +87,14 @@
 #define PCIE_ECAP_CIDR_MASK	0xffff
 #define PCIE_ECAP_NCPR_MASK	0xfff
 
+/* PCIe Extended Capability Header */
+#define PCIE_ECH_ID_SHIFT			U(0)
+#define PCIE_ECH_ID_WIDTH			U(16)
+#define PCIE_ECH_CAP_VER_SHIFT			U(16)
+#define PCIE_ECH_CAP_VER_WIDTH			U(4)
+#define PCIE_ECH_NEXT_CAP_OFFSET_SHIFT		U(20)
+#define PCIE_ECH_NEXT_CAP_OFFSET_WIDTH		U(12)
+
 #define PCIE_CAP_START		0x40
 #define PCIE_CAP_END		0xFC
 #define PCIE_ECAP_START		0x100
@@ -107,6 +115,8 @@
 #define ECID_PASID		0x001B
 #define ECID_DPC		0x001D
 #define ECID_DVSEC		0x0023
+#define ECID_DOE		0x002E
+#define ECID_IDE		0x0030
 
 /* PCI Express capability struct offsets */
 #define CIDR_OFFSET		0x0
diff --git a/lib/pcie/pcie_dvsec_rmeda.c b/lib/pcie/pcie_dvsec_rmeda.c
new file mode 100644
index 0000000..c1da99a
--- /dev/null
+++ b/lib/pcie/pcie_dvsec_rmeda.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <common_def.h>
+#include <errno.h>
+#include <stddef.h>
+#include <debug.h>
+#include <mmio.h>
+#include <pcie.h>
+#include <pcie_spec.h>
+#include <pcie_dvsec_rmeda.h>
+#include <platform.h>
+#include <tftf_lib.h>
+
+/*
+ * Check if the DVSEC header matches RME Sys Arch spec
+ *
+ * DVSEC_REVISION must be 0
+ * DVSEC_VENDOR_ID must be Arm
+ * DVSEC_ID must be RME_DA
+ */
+static bool is_dvsec_arm_rmeda(uint32_t rp_bdf, uint32_t dvsec_base)
+{
+	uint32_t hdr1 = 0U;
+	uint32_t hdr2 = 0U;
+
+	hdr1 = pcie_read_cfg(rp_bdf, dvsec_base + PCIE_ECAP_DVSEC_HDR1_OFFSET);
+	hdr2 = pcie_read_cfg(rp_bdf, dvsec_base + PCIE_ECAP_DVSEC_HDR2_OFFSET);
+
+	if ((EXTRACT(DVSEC_HDR1_VENDOR_ID, hdr1) == DVSEC_VENDOR_ID_ARM) &&
+	    (EXTRACT(DVSEC_HDR1_REVISION, hdr1) == DVSEC_REVISION_0) &&
+	    (EXTRACT(DVSEC_HDR2_DVSEC_ID, hdr2) == DVSEC_ID_RME_DA)) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Traverse all DVSEC extended capability and return the first RMEDA DVSEC if the
+ * header, revision are expected as mentioned in RME System Architecture.
+ */
+uint32_t pcie_find_rmeda_capability(uint32_t rp_bdf, uint32_t *cid_offset)
+{
+	uint32_t ech;
+	unsigned int dvsec_offset;
+	uint16_t next_cap_offset;
+
+	dvsec_offset = PCIE_ECAP_START;
+	do {
+		assert((dvsec_offset + sizeof(ech)) < SZ_4K);
+
+		ech = pcie_read_cfg(rp_bdf, dvsec_offset);
+
+		/* Check for PCIE_ECH_CAP_VER_1 as well? */
+		if ((EXTRACT(PCIE_ECH_ID, ech) == ECID_DVSEC) &&
+		    is_dvsec_arm_rmeda(rp_bdf, dvsec_offset)) {
+			*cid_offset = dvsec_offset;
+			return PCIE_SUCCESS;
+		}
+
+		next_cap_offset = EXTRACT(PCIE_ECH_NEXT_CAP_OFFSET, ech);
+		dvsec_offset += next_cap_offset;
+	} while ((next_cap_offset != 0U) && (dvsec_offset < PCIE_ECAP_END));
+
+	return PCIE_CAP_NOT_FOUND;
+}
diff --git a/tftf/tests/tests-realm-payload.mk b/tftf/tests/tests-realm-payload.mk
index add6a7f..5859d31 100644
--- a/tftf/tests/tests-realm-payload.mk
+++ b/tftf/tests/tests-realm-payload.mk
@@ -62,6 +62,7 @@
 	$(addprefix lib/pcie/,	\
 		pcie.c		\
 		pcie_doe.c	\
+		pcie_dvsec_rmeda.c	\
 	)
 
 ifeq (${ENABLE_REALM_PAYLOAD_TESTS},1)