feat(lib/pcie): init pcie device capabilities

Add additional fields in pcie_dev structure that will be later
used by DA testcases.

Find and initialize devices extended capabilities.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I32042393dc023d8ac948aa9f4ea921de22ec0b98
diff --git a/include/lib/pcie/pcie.h b/include/lib/pcie/pcie.h
index 7dd165c..a08cd4a 100644
--- a/include/lib/pcie/pcie.h
+++ b/include/lib/pcie/pcie.h
@@ -7,6 +7,7 @@
 #ifndef PCIE_H
 #define PCIE_H
 
+#include <stdbool.h>
 #include <cdefs.h>
 #include <stdint.h>
 #include <utils_def.h>
@@ -14,6 +15,8 @@
 /* platforms need to ensure that number of entries is less that this value */
 #define MAX_PCIE_INFO_ENTRIES 5
 
+#define PCIE_DEVICES_MAX	128
+
 typedef struct {
 	unsigned long ecam_base;	/* ECAM base address */
 	unsigned int segment_num;	/* Segment number of this ECAM */
@@ -26,14 +29,39 @@
 	pcie_info_block_t block[MAX_PCIE_INFO_ENTRIES];
 };
 
-typedef struct {
-	uint32_t bdf;
-	uint32_t rp_bdf;
-} pcie_device_attr_t;
+/* Flags for PCIe device capability */
+#define PCIE_DEV_CFLAG_DOE		(U(1) << 0)
+#define PCIE_DEV_CFLAG_IDE		(U(1) << 1)
+#define PCIE_DEV_CFLAG_DVSEC_RMEDA	(U(1) << 3)
 
-typedef struct __packed {
+#define pcie_dev_has_doe(_d)	(((_d)->cflags & PCIE_DEV_CFLAG_DOE) != 0U)
+#define pcie_dev_has_ide(_d)	(((_d)->cflags & PCIE_DEV_CFLAG_IDE) != 0U)
+#define pcie_dev_has_dvsec_rmeda(_d)	\
+	(((_d)->cflags & PCIE_DEV_CFLAG_DVSEC_RMEDA) != 0U)
+
+struct pcie_dev {
+	uint32_t bdf;
+
+	/* Pointer to rootport device if this is a endpoint */
+	struct pcie_dev *rp_dev;
+
+	/* PCIe capabilities flags */
+	uint32_t cflags;
+
+	uint32_t doe_cap_base;
+	uint32_t ide_cap_base;
+	uint32_t dvsec_rmeda_cap_base;
+
+	/* Device port type */
+	uint32_t dp_type;
+
+	unsigned long ecam_base;
+};
+typedef struct pcie_dev pcie_dev_t;
+
+typedef struct {
 	uint32_t num_entries;
-	pcie_device_attr_t device[]; /* in the format of Segment/Bus/Dev/Func */
+	pcie_dev_t device[PCIE_DEVICES_MAX];
 } pcie_device_bdf_table_t;
 
 /* Address initialisation structure */
@@ -71,9 +99,6 @@
 #define PCIE_CAP_NOT_FOUND	0x10000010  /* The specified capability was not found */
 #define PCIE_UNKNOWN_RESPONSE	0xFFFFFFFF  /* Function not found or UR response from completer */
 
-/* Allows storage of 2048 valid BDFs */
-#define PCIE_DEVICE_BDF_TABLE_SZ	8192
-
 typedef enum {
 	HEADER = 0,
 	PCIE_CAP = 1,
diff --git a/lib/pcie/pcie.c b/lib/pcie/pcie.c
index ea187a6..96e1fb9 100644
--- a/lib/pcie/pcie.c
+++ b/lib/pcie/pcie.c
@@ -13,6 +13,7 @@
 #include <pcie.h>
 #include <pcie_doe.h>
 #include <pcie_spec.h>
+#include <pcie_dvsec_rmeda.h>
 #include <platform.h>
 #include <plat_pcie_enum.h>
 #include <tftf_lib.h>
@@ -21,8 +22,7 @@
 
 const struct pcie_info_table *g_pcie_info_table;
 static pcie_device_bdf_table_t *g_pcie_bdf_table;
-
-static pcie_device_bdf_table_t pcie_bdf_table[PCIE_DEVICE_BDF_TABLE_SZ];
+static pcie_device_bdf_table_t pcie_bdf_table;
 
 static uint32_t g_pcie_index;
 static uint32_t g_enumerate;
@@ -252,13 +252,13 @@
  * @brief  Returns BDF of the upstream Root Port of a pcie device function.
  *
  * @param  bdf	     - Function's Segment/Bus/Dev/Func in PCIE_CREATE_BDF format
- * @param  usrp_bdf  - Upstream Rootport bdf in PCIE_CREATE_BDF format
- * @return 0 for success, 1 for failure.
+ * @return pcie_dev for success, NULL for failure.
  */
-static uint32_t pcie_get_rootport(uint32_t bdf, uint32_t *rp_bdf)
+static pcie_dev_t *pcie_get_rootport(uint32_t bdf)
 {
 	uint32_t seg_num, sec_bus, sub_bus;
 	uint32_t reg_value, dp_type, index = 0;
+	uint32_t rp_bdf;
 
 	dp_type = pcie_device_port_type(bdf);
 
@@ -266,43 +266,43 @@
 
 	/* If the device is RP or iEP_RP, set its rootport value to same */
 	if ((dp_type == RP) || (dp_type == iEP_RP)) {
-		*rp_bdf = bdf;
-		return 0;
+		return NULL;
 	}
 
 	/* If the device is RCiEP and RCEC, set RP as 0xff */
 	if ((dp_type == RCiEP) || (dp_type == RCEC)) {
-		*rp_bdf = 0xffffffff;
-		return 1;
+		return NULL;
 	}
 
 	assert(g_pcie_bdf_table != NULL);
 
 	while (index < g_pcie_bdf_table->num_entries) {
-		*rp_bdf = g_pcie_bdf_table->device[index++].bdf;
+		rp_bdf = g_pcie_bdf_table->device[index].bdf;
 
 		/*
 		 * Extract Secondary and Subordinate Bus numbers of the
 		 * upstream Root port and check if the input function's
 		 * bus number falls within that range.
 		 */
-		reg_value = pcie_read_cfg(*rp_bdf, TYPE1_PBN);
-		seg_num = PCIE_EXTRACT_BDF_SEG(*rp_bdf);
+		reg_value = pcie_read_cfg(rp_bdf, TYPE1_PBN);
+		seg_num = PCIE_EXTRACT_BDF_SEG(rp_bdf);
 		sec_bus = ((reg_value >> SECBN_SHIFT) & SECBN_MASK);
 		sub_bus = ((reg_value >> SUBBN_SHIFT) & SUBBN_MASK);
-		dp_type = pcie_device_port_type(*rp_bdf);
+		dp_type = pcie_device_port_type(rp_bdf);
 
 		if (((dp_type == RP) || (dp_type == iEP_RP)) &&
-			(sec_bus <= PCIE_EXTRACT_BDF_BUS(bdf)) &&
-			(sub_bus >= PCIE_EXTRACT_BDF_BUS(bdf)) &&
-			(seg_num == PCIE_EXTRACT_BDF_SEG(bdf)))
-			return 0;
+		    (sec_bus <= PCIE_EXTRACT_BDF_BUS(bdf)) &&
+		    (sub_bus >= PCIE_EXTRACT_BDF_BUS(bdf)) &&
+		    (seg_num == PCIE_EXTRACT_BDF_SEG(bdf))) {
+			return &g_pcie_bdf_table->device[index];
 		}
 
-		/* Return failure */
-		ERROR("PCIe Hierarchy fail: RP of bdf 0x%x not found\n", bdf);
-		*rp_bdf = 0;
-		return 1;
+		index++;
+	}
+
+	/* Return failure */
+	ERROR("PCIe Hierarchy fail: RP of bdf 0x%x not found\n", bdf);
+	return NULL;
 }
 
 /*
@@ -313,8 +313,9 @@
  */
 static uint32_t pcie_populate_device_rootport(void)
 {
-	uint32_t bdf, rp_bdf;
+	uint32_t bdf;
 	pcie_device_bdf_table_t *bdf_tbl_ptr = g_pcie_bdf_table;
+	pcie_dev_t *rp_dev;
 
 	assert(bdf_tbl_ptr != NULL);
 
@@ -323,16 +324,125 @@
 		bdf = bdf_tbl_ptr->device[tbl_index].bdf;
 
 		/* Checks if the BDF has RootPort */
-		pcie_get_rootport(bdf, &rp_bdf);
+		rp_dev = pcie_get_rootport(bdf);
 
-		bdf_tbl_ptr->device[tbl_index].rp_bdf = rp_bdf;
-		PCIE_DEBUG("Dev bdf: 0x%x RP bdf: 0x%x\n", bdf, rp_bdf);
+		bdf_tbl_ptr->device[tbl_index].rp_dev = rp_dev;
+
+		if (rp_dev != NULL) {
+			INFO("Dev bdf: 0x%x RP bdf: 0x%x\n", bdf,
+			     rp_dev->bdf);
+		} else {
+			INFO("Dev bdf: 0x%x RP bdf: none\n", bdf);
+		}
 	}
 
 	return 0;
 }
 
 /*
+ * @brief  Returns the header type of the input pcie device function
+ *
+ * @param  bdf   - Segment/Bus/Dev/Func in the format of PCIE_CREATE_BDF
+ * @return TYPE0_HEADER for functions with Type 0 config space header,
+ *         TYPE1_HEADER for functions with Type 1 config space header,
+ */
+static uint32_t pcie_function_header_type(uint32_t bdf)
+{
+	/* Read four bytes of config space starting from cache line size register */
+	uint32_t reg_value = pcie_read_cfg(bdf, TYPE01_CLSR);
+
+	/* Extract header type register value */
+	reg_value = ((reg_value >> TYPE01_HTR_SHIFT) & TYPE01_HTR_MASK);
+
+	/* Header layout bits within header type register indicate the header type */
+	return ((reg_value >> HTR_HL_SHIFT) & HTR_HL_MASK);
+}
+
+/*
+ * @brief  Returns the ECAM address of the input PCIe function
+ *
+ * @param  bdf   - Segment/Bus/Dev/Func in PCIE_CREATE_BDF format
+ * @return ECAM address if success, else NULL address
+ */
+static uintptr_t pcie_get_ecam_base(uint32_t bdf)
+{
+	uint8_t ecam_index = 0, sec_bus = 0, sub_bus;
+	uint16_t seg_num = (uint16_t)PCIE_EXTRACT_BDF_SEG(bdf);
+	uint32_t reg_value;
+	uintptr_t ecam_base = 0;
+
+	assert(g_pcie_info_table != NULL);
+
+	while (ecam_index < g_pcie_info_table->num_entries) {
+		/* Derive ECAM specific information */
+		const pcie_info_block_t *block = &g_pcie_info_table->block[ecam_index];
+
+		if (seg_num == block->segment_num) {
+			if (pcie_function_header_type(bdf) == TYPE0_HEADER) {
+				/* Return ecam_base if Type0 Header */
+				ecam_base = block->ecam_base;
+				break;
+			}
+
+			/* Check for Secondary/Subordinate bus if Type1 Header */
+			reg_value = pcie_read_cfg(bdf, TYPE1_PBN);
+			sec_bus = ((reg_value >> SECBN_SHIFT) & SECBN_MASK);
+			sub_bus = ((reg_value >> SUBBN_SHIFT) & SUBBN_MASK);
+
+			if ((sec_bus >= block->start_bus_num) &&
+			    (sub_bus <= block->end_bus_num)) {
+				ecam_base = block->ecam_base;
+				break;
+			}
+		}
+		ecam_index++;
+	}
+
+	return ecam_base;
+}
+
+static void pcie_devices_init_fields(void)
+{
+	pcie_device_bdf_table_t *bdf_tbl_ptr = g_pcie_bdf_table;
+	pcie_dev_t *pcie_dev;
+	uint32_t status;
+	uint32_t base;
+	uint32_t bdf;
+
+	assert(bdf_tbl_ptr != NULL);
+
+	for (uint32_t i = 0; i < bdf_tbl_ptr->num_entries; i++) {
+		pcie_dev = &bdf_tbl_ptr->device[i];
+		bdf = pcie_dev->bdf;
+
+		pcie_dev->dp_type = pcie_device_port_type(bdf);
+		pcie_dev->ecam_base = pcie_get_ecam_base(bdf);
+
+		/* Has DOE? */
+		status = pcie_find_capability(bdf, PCIE_ECAP, ECID_DOE, &base);
+		if (status == PCIE_SUCCESS) {
+			pcie_dev->cflags |= PCIE_DEV_CFLAG_DOE;
+			pcie_dev->doe_cap_base = base;
+		}
+
+		/* Has IDE? */
+		status = pcie_find_capability(bdf, PCIE_ECAP, ECID_IDE, &base);
+		if (status == PCIE_SUCCESS) {
+			pcie_dev->cflags |= PCIE_DEV_CFLAG_IDE;
+			pcie_dev->ide_cap_base = base;
+		}
+
+		if (pcie_dev->dp_type == RP) {
+			status = pcie_find_rmeda_capability(bdf, &base);
+			if (status == PCIE_SUCCESS) {
+				pcie_dev->cflags |= PCIE_DEV_CFLAG_DVSEC_RMEDA;
+				pcie_dev->dvsec_rmeda_cap_base = base;
+			}
+		}
+	}
+}
+
+/*
  * @brief  Returns the BDF Table pointer
  *
  * @param  None
@@ -405,6 +515,8 @@
 
 						g_pcie_bdf_table->device[
 							g_pcie_bdf_table->num_entries++].bdf = bdf;
+
+						assert(g_pcie_bdf_table->num_entries < PCIE_DEVICES_MAX);
 					}
 				}
 			}
@@ -413,72 +525,17 @@
 
 	/* Sanity Check : Confirm all EP (normal, integrated) have a rootport */
 	pcie_populate_device_rootport();
+
+	/*
+	 * Once devices are enumerated and rootports are assigned, initialize
+	 * the rest of pcie_dev fields
+	 */
+	pcie_devices_init_fields();
+
 	INFO("Number of BDFs found     : %u\n", g_pcie_bdf_table->num_entries);
 }
 
 /*
- * @brief  Returns the header type of the input pcie device function
- *
- * @param  bdf   - Segment/Bus/Dev/Func in the format of PCIE_CREATE_BDF
- * @return TYPE0_HEADER for functions with Type 0 config space header,
- *         TYPE1_HEADER for functions with Type 1 config space header,
- */
-static uint32_t pcie_function_header_type(uint32_t bdf)
-{
-	/* Read four bytes of config space starting from cache line size register */
-	uint32_t reg_value = pcie_read_cfg(bdf, TYPE01_CLSR);
-
-	/* Extract header type register value */
-	reg_value = ((reg_value >> TYPE01_HTR_SHIFT) & TYPE01_HTR_MASK);
-
-	/* Header layout bits within header type register indicate the header type */
-	return ((reg_value >> HTR_HL_SHIFT) & HTR_HL_MASK);
-}
-
-/*
- * @brief  Returns the ECAM address of the input PCIe function
- *
- * @param  bdf   - Segment/Bus/Dev/Func in PCIE_CREATE_BDF format
- * @return ECAM address if success, else NULL address
- */
-static uintptr_t pcie_get_ecam_base(uint32_t bdf)
-{
-	uint8_t ecam_index = 0, sec_bus = 0, sub_bus;
-	uint16_t seg_num = (uint16_t)PCIE_EXTRACT_BDF_SEG(bdf);
-	uint32_t reg_value;
-	uintptr_t ecam_base = 0;
-
-	assert(g_pcie_info_table != NULL);
-
-	while (ecam_index < g_pcie_info_table->num_entries) {
-		/* Derive ECAM specific information */
-		const pcie_info_block_t *block = &g_pcie_info_table->block[ecam_index];
-
-		if (seg_num == block->segment_num) {
-			if (pcie_function_header_type(bdf) == TYPE0_HEADER) {
-				/* Return ecam_base if Type0 Header */
-				ecam_base = block->ecam_base;
-				break;
-			}
-
-			/* Check for Secondary/Subordinate bus if Type1 Header */
-			reg_value = pcie_read_cfg(bdf, TYPE1_PBN);
-			sec_bus = ((reg_value >> SECBN_SHIFT) & SECBN_MASK);
-			sub_bus = ((reg_value >> SUBBN_SHIFT) & SUBBN_MASK);
-
-			if ((sec_bus >= block->start_bus_num) &&
-			    (sub_bus <= block->end_bus_num)) {
-				ecam_base = block->ecam_base;
-				break;
-			}
-		}
-		ecam_index++;
-	}
-
-	return ecam_base;
-}
-
-/*
  * @brief   This API prints all the PCIe Devices info
  * 1. Caller	   -  Validation layer.
  * 2. Prerequisite -  val_pcie_create_info_table()
@@ -620,7 +677,7 @@
 	unsigned int num_ecam;
 
 	INFO("Creating PCIe info table\n");
-	g_pcie_bdf_table = pcie_bdf_table;
+	g_pcie_bdf_table = &pcie_bdf_table;
 
 	num_ecam = g_pcie_info_table->num_entries;
 	INFO("Number of ECAM regions   : %u\n", num_ecam);