From b0586b5938e9d371e55671422b2f0a5d2cd10c54 Mon Sep 17 00:00:00 2001
From: Michael Bringmann <mwb@linux.vnet.ibm.com>
Date: Wed, 2 Oct 2019 16:54:52 -0500
Subject: [PATCH] powerpc-utils/devtree: Parse 'ibm,drc-info' property

Parse new DRC Info: Define data structures to support parsing
the new "ibm,drc-info" device tree property.  Integrate the new
property information into the existing search mechanisms of the
userspace 'drmgr' driver.

Signed-off-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 src/drmgr/common_ofdt.c | 223 ++++++++++++++++++++++++++++++++++------
 1 file changed, 194 insertions(+), 29 deletions(-)

diff --git a/src/drmgr/common_ofdt.c b/src/drmgr/common_ofdt.c
index 8c9e224..c110bc0 100644
--- a/src/drmgr/common_ofdt.c
+++ b/src/drmgr/common_ofdt.c
@@ -41,6 +41,16 @@ struct drc_prop_grp {
 	struct of_list_prop drc_domains;
 };
 
+struct drc_info {
+	char	*drc_type;
+	char	*drc_name_prefix;
+	int	drc_index_start;
+	int	drc_name_suffix_start;
+	int	n_seq_elems;
+	int	seq_inc;
+	int	drc_power_domain;
+};
+
 struct dr_connector *all_drc_lists = NULL;
 
 /**
@@ -186,6 +196,169 @@ build_connectors_list(struct drc_prop_grp *group, int n_entries,
 	return 0;
 }
 
+/**
+ * drc_info_connectors_v1
+ *
+ * @param full_path
+ * @param ofdt_path
+ * @param list
+ * @returns 0 on success, !0 otherwise
+ */
+static int drc_info_connectors_v1(char *full_path, char *ofdt_path,
+				struct dr_connector **list)
+{
+	struct dr_connector *out_list = NULL;
+	struct drc_prop_grp prop_grp;
+	struct of_list_prop *drc_names;
+	int n_drcs;
+	int rc = 0;
+
+	rc = get_drc_prop_grp(full_path, &prop_grp);
+	if (rc) {
+		say(DEBUG,
+		    "Could not find DRC property group in path: %s.\n",
+		    full_path);
+		goto done;
+	}
+
+	drc_names = &prop_grp.drc_names;
+	n_drcs = drc_names->n_entries;
+
+	out_list = zalloc(n_drcs * sizeof(struct dr_connector));
+	if (out_list == NULL)
+		goto done;
+
+	build_connectors_list(&prop_grp, n_drcs, out_list);
+
+done:
+	if (rc) {
+		free_drc_props(&prop_grp);
+		free(out_list);
+	} else {
+		snprintf(out_list->ofdt_path, DR_PATH_MAX, "%s", ofdt_path);
+		*list = out_list;
+	}
+
+	return rc;
+}
+
+/**
+ * drc_info_connectors_v2
+ *
+ * @param full_path
+ * @param ofdt_path
+ * @param list
+ * @returns 0 on success, !0 otherwise
+ */
+static int drc_info_connectors_v2(char *full_path, char *ofdt_path,
+				struct dr_connector **list)
+{
+	struct dr_connector *out_list = NULL;
+	struct drc_info info;
+	char *prop_name = "ibm,drc-info";
+	char *prop_data, *data_ptr;
+	int i, j, n_entries, size, connector_size, ics, rc;
+
+	size = get_property_size(full_path, prop_name);
+	prop_data = zalloc(size);
+	if (prop_data == NULL)
+		return -1;
+	rc = get_property(full_path, prop_name, prop_data, size);
+	if (rc) {
+		free(prop_data);
+		return -1;
+	}
+
+	/* Num of DRC-info sets */
+	data_ptr = prop_data;
+	n_entries  = be32toh(*(uint *)data_ptr);
+	data_ptr += 4;
+
+	/* Extract drc-info data */
+	for (j = 0, connector_size = 0; j < n_entries; j++) {
+		info.drc_type = data_ptr;
+		data_ptr += strlen(info.drc_type)+1;
+		info.drc_name_prefix = data_ptr;
+		data_ptr += strlen(info.drc_name_prefix)+1;
+		data_ptr += 4;	/* Skip drc-index-start */
+		data_ptr += 4;	/* Skip drc-name-suffix-start */
+		info.n_seq_elems = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;	/* Advance over n-seq-elems */
+		data_ptr += 4;	/* Skip sequential-increment */
+		data_ptr += 4;	/* Skip drc-power-domain */
+		if (info.n_seq_elems <= 0)
+			continue;
+		connector_size += info.n_seq_elems;
+	}
+
+	/* Allocate list entry */
+	out_list = zalloc(connector_size * sizeof(struct dr_connector));
+	if (out_list == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	/* Extract drc-info data */
+	data_ptr = prop_data;
+	data_ptr += 4;
+	for (j = 0, ics = 0; j < n_entries; j++) {
+		info.drc_type = data_ptr;
+		data_ptr += strlen(info.drc_type)+1;
+		info.drc_name_prefix = data_ptr;
+		data_ptr += strlen(info.drc_name_prefix)+1;
+
+		info.drc_index_start  = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;
+
+		info.drc_name_suffix_start = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;
+
+		info.n_seq_elems = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;
+
+		info.seq_inc = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;
+
+		info.drc_power_domain = be32toh(*(uint *)data_ptr);
+		data_ptr += 4;
+
+		/* Build connector list */
+		if (info.n_seq_elems <= 0)
+			continue;
+
+		for (i = 0; i < info.n_seq_elems; i++, ics++) {
+			out_list[ics].index = info.drc_index_start+
+					 (i*info.seq_inc);
+			out_list[ics].powerdomain = info.drc_power_domain;
+
+			sprintf(out_list[ics].name, "%s%d",
+				info.drc_name_prefix,
+				info.drc_name_suffix_start+(i*info.seq_inc));
+
+			strncpy(out_list[ics].type, info.drc_type, DRC_STR_MAX);
+
+			out_list[ics].next = &out_list[ics+1];
+		}
+	}
+        if (ics > 0)
+                out_list[ics-1].next = NULL;
+
+done:
+	if (prop_data)
+		free(prop_data);
+
+	if (rc) {
+		free(out_list);
+		*list = NULL;
+	} else {
+		snprintf(out_list->ofdt_path, DR_PATH_MAX, "%s", ofdt_path);
+		*list = out_list;
+	}
+
+	return rc;
+}
+
+
 /**
  * of_to_full_path
  *
@@ -232,11 +405,12 @@ of_to_full_path(const char *of_path)
 struct dr_connector *
 get_drc_info(const char *of_path)
 {
-	struct dr_connector *list = NULL;
-	struct of_list_prop *drc_names;
-	struct drc_prop_grp prop_grp;
+	struct stat sbuf;
+	char fname[DR_PATH_MAX];
+	char ofdt_path[DR_PATH_MAX];
 	char *full_path = NULL;
-	int rc, n_drcs;
+	struct dr_connector *list = NULL;
+	int rc;
 
 	for (list = all_drc_lists; list; list = list->all_next) {
 		if (! strcmp(list->ofdt_path, of_path))
@@ -246,33 +420,24 @@ get_drc_info(const char *of_path)
 	full_path = of_to_full_path(of_path);
 	if (full_path == NULL)
 		return NULL;
-	
-	rc = get_drc_prop_grp(full_path, &prop_grp);
-	if (rc) {
-		say(DEBUG, "Could not find DRC property group in path: %s.\n",
-			full_path);
-		goto done;
-	}
 
-	drc_names = &prop_grp.drc_names;
-	n_drcs = drc_names->n_entries;
-
-	list = zalloc(n_drcs * sizeof(struct dr_connector));
-	if (list == NULL)
-		goto done;
-
-	/* XXX Unchecked rc */
-	rc = build_connectors_list(&prop_grp, n_drcs, list);
-
-	snprintf(list->ofdt_path, DR_PATH_MAX, "%s", of_path);
-	
-	list->all_next = all_drc_lists;
-	all_drc_lists = list;
+	/* ibm,drc-info vs the old implementation */
+	sprintf(fname, "%s/%s", full_path, "ibm,drc-info");
+	snprintf(ofdt_path, DR_PATH_MAX, "%s", of_path);
+	rc = stat(fname, &sbuf);
+	if (rc)
+		rc = drc_info_connectors_v1(full_path, ofdt_path, &list);
+	else
+		rc = drc_info_connectors_v2(full_path, ofdt_path, &list);
 
-done:
-	free_drc_props(&prop_grp);
-	if (full_path)
-		free(full_path);
+	if (rc == 0) {
+		list->all_next = all_drc_lists;
+		all_drc_lists = list;
+	} else {
+		if (full_path)
+			free(full_path);
+		list = NULL;
+	}
 
 	return list;
 }
