[PATCH v14 09/32] firmware: arm_scmi: Add Telemetry groups initialization

Cristian Marussi cristian.marussi at arm.com
Fri Oct 16 11:55:30 PDT 2026


Allocate and initialize Telemetry Groups from base DataEvent data.

Signed-off-by: Cristian Marussi <cristian.marussi at arm.com>
---
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the
  groups initialization code.
---
 drivers/firmware/arm_scmi/telemetry.c | 149 +++++++++++++++++++++++++-
 include/linux/scmi_protocol.h         |  20 ++++
 2 files changed, 167 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
index 87ff75805935..9e3dc2c2cc4b 100644
--- a/drivers/firmware/arm_scmi/telemetry.c
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -123,6 +123,7 @@ struct telemetry_info {
 	unsigned int default_blk_ts_rate;
 	const struct scmi_protocol_handle *ph;
 	struct telemetry_de *tdes;
+	struct scmi_telemetry_group *grps;
 	struct xarray xa_des;
 	/* Mutex to protect access to @free_des */
 	struct mutex free_mtx;
@@ -321,6 +322,7 @@ static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti,
 {
 	const struct scmi_de_desc *desc = *next;
 	size_t payld_sz = sizeof(*desc);
+	unsigned int grp_id;
 
 	lockdep_assert_held(&tde->mtx);
 
@@ -328,7 +330,22 @@ static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti,
 		return -ENOSPC;
 
 	tde->de.info->id = le32_to_cpu(desc->de_id);
-	tde->de.info->grp_id = le32_to_cpu(desc->group_id);
+	grp_id = le32_to_cpu(desc->group_id);
+	if (grp_id != SCMI_TLM_GRP_INVALID) {
+		struct scmi_telemetry_res_info *rinfo;
+
+		/* Get rinfo without triggering a recursive enumeration */
+		rinfo = __scmi_telemetry_resources_get(ti);
+
+		/* Group descriptors are empty but allocated at this point */
+		if (grp_id >= ti->info.base.num_groups)
+			return -EINVAL;
+
+		/* Link to parent group */
+		tde->de.grp = &rinfo->grps[grp_id];
+	}
+
+	tde->de.info->grp_id = grp_id;
 	tde->de.info->data_sz = le32_to_cpu(desc->de_data_sz);
 	tde->de.info->type = GET_DE_TYPE(desc);
 	tde->de.info->unit = GET_DE_UNIT(desc);
@@ -459,6 +476,96 @@ static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,
 	if (tde->de.fc_support)
 		list_add(&tde->item, &ti->fcs_des);
 
+	/* Account for this DE in group num_de counter */
+	if (tde->de.grp)
+		tde->de.grp->info->num_des++;
+
+	return 0;
+}
+
+static int
+scmi_telemetry_de_groups_init(struct device *dev, struct telemetry_info *ti)
+{
+	struct scmi_telemetry_res_info *rinfo;
+	unsigned int num_groups = 0;
+
+	/* Get rinfo without triggering a recursive enumeration */
+	rinfo = __scmi_telemetry_resources_get(ti);
+
+	/* Allocate all groups DEs IDs arrays at first ... */
+	for (int i = 0; i < ti->info.base.num_groups; i++, num_groups++) {
+		struct scmi_telemetry_group *grp = &rinfo->grps[i];
+		size_t des_str_sz;
+
+		unsigned int *des __free(kfree) =
+			kzalloc_objs(unsigned int, grp->info->num_des);
+		if (!des)
+			break;
+
+		/*
+		 * Max size 32bit ID string in Hex: 0xCAFECAFE
+		 *  - 10 digits + ' '/'\n' = 11 bytes per  number
+		 *  - terminating NUL character
+		 */
+		des_str_sz = grp->info->num_des * 11 + 1;
+		char *des_str __free(kfree) = kzalloc_objs(*des_str, des_str_sz);
+		if (!des_str)
+			break;
+
+		grp->des = no_free_ptr(des);
+		grp->des_str = no_free_ptr(des_str);
+		/* Reset group DE counter */
+		grp->info->num_des = 0;
+	}
+
+	/* Unroll on failure... */
+	if (num_groups < ti->info.base.num_groups) {
+		for (int i = num_groups - 1; i >= 0; i--) {
+			kfree(rinfo->grps[i].des);
+			rinfo->grps[i].des = NULL;
+			kfree(rinfo->grps[i].des_str);
+			rinfo->grps[i].des_str = NULL;
+		}
+
+		return -ENOMEM;
+	}
+
+	/* Scan DEs and populate DE IDs arrays for all groups */
+	for (int i = 0; i < rinfo->num_des; i++) {
+		struct scmi_telemetry_group *grp;
+
+		grp = rinfo->des[i]->grp;
+		if (!grp)
+			continue;
+
+		/*
+		 * Note that, at this point, num_des is guaranteed to be
+		 * sane (in-bounds) by construction.
+		 */
+		grp->des[grp->info->num_des++] = i;
+	}
+
+	/* Build composing DES string */
+	for (int i = 0; i < ti->info.base.num_groups; i++) {
+		struct scmi_telemetry_group *grp = &rinfo->grps[i];
+		size_t bufsize = grp->info->num_des * 11 + 1;
+		char *buf = grp->des_str;
+
+		for (int j = 0; j < grp->info->num_des; j++) {
+			char term = j != (grp->info->num_des - 1) ? ' ' : '\0';
+			int len;
+
+			len = scnprintf(buf, bufsize, "0x%08X%c",
+					rinfo->des[grp->des[j]]->info->id, term);
+
+			buf += len;
+			bufsize -= len;
+		}
+	}
+
+	/* Expose all groups once all fully initialized */
+	rinfo->num_groups = num_groups;
+
 	return 0;
 }
 
@@ -491,7 +598,7 @@ static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti)
 	if (ret)
 		return ret;
 
-	return 0;
+	return scmi_telemetry_de_groups_init(ph->dev, ti);
 }
 
 static const struct scmi_telemetry_info *
@@ -561,6 +668,18 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)
 	if (!dei_store)
 		return -ENOMEM;
 
+	/* Array to hold descriptors of discovered GROUPs */
+	struct scmi_telemetry_group *grps __free(kfree) =
+		kzalloc_objs(*grps, ti->info.base.num_groups);
+	if (!grps)
+		return -ENOMEM;
+
+	/* Allocate a set of contiguous Group info descriptors. */
+	struct scmi_telemetry_grp_info *grps_store __free(kfree) =
+		kzalloc_objs(*grps_store, ti->info.base.num_groups);
+	if (!grps_store)
+		return -ENOMEM;
+
 	struct scmi_telemetry_res_info *rinfo __free(kfree) =
 		kzalloc_obj(*rinfo);
 	if (!rinfo)
@@ -577,12 +696,20 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)
 		scmi_telemetry_free_tde_put(ti, &tdes[i]);
 	}
 
+	for (int i = 0; i < ti->info.base.num_groups; i++) {
+		grps_store[i].grp_id = i;
+		/* Bind contiguous Group info struct */
+		grps[i].info = &grps_store[i];
+	}
+
 	INIT_LIST_HEAD(&ti->fcs_des);
 
 	ti->tdes = no_free_ptr(tdes);
 
 	rinfo->des = no_free_ptr(des);
 	rinfo->dei_store = no_free_ptr(dei_store);
+	rinfo->grps = no_free_ptr(grps);
+	rinfo->grps_store = no_free_ptr(grps_store);
 
 	/* Ensure all of the above assignments are visible */
 	smp_store_release(&ti->rinfo, no_free_ptr(rinfo));
@@ -611,6 +738,9 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)
 	kfree(rinfo->des);
 	kfree(rinfo->dei_store);
 
+	kfree(rinfo->grps);
+	kfree(rinfo->grps_store);
+
 	kfree(rinfo);
 
 	dev_dbg(ti->ph->dev, "SCMI Telemetry resources freed for instance\n");
@@ -632,10 +762,25 @@ static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)
 	xa_destroy(&ti->xa_des);
 }
 
+static void scmi_telemetry_groups_free(struct scmi_telemetry_res_info *rinfo)
+{
+	/* Cannot be NULL here by construction, make sure it remains like that */
+	if (WARN_ON(!rinfo))
+		return;
+
+	for (int i = 0; i < rinfo->num_groups; i++) {
+		struct scmi_telemetry_group *grp = &rinfo->grps[i];
+
+		kfree(grp->des);
+		kfree(grp->des_str);
+	}
+}
+
 static void scmi_telemetry_instance_cleanup(void *arg)
 {
 	struct telemetry_info *ti = arg;
 
+	scmi_telemetry_groups_free(ti->rinfo);
 	scmi_telemetry_tdes_cleanup(ti);
 	scmi_telemetry_resources_free(ti);
 }
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 018427daf55f..51d4cbfc0969 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -826,6 +826,22 @@ struct scmi_pinctrl_proto_ops {
 	int (*pin_free)(const struct scmi_protocol_handle *ph, u32 pin);
 };
 
+#define SCMI_TLM_GRP_INVALID		0xFFFFFFFF
+
+struct scmi_telemetry_grp_info {
+	unsigned int grp_id;
+	unsigned int num_des;
+	unsigned int num_intervals;
+};
+
+struct scmi_telemetry_group {
+	bool enabled;
+	bool tstamp_enabled;
+	unsigned int *des;
+	char *des_str;
+	struct scmi_telemetry_grp_info *info;
+};
+
 struct scmi_telemetry_de_info {
 	unsigned int id;
 	unsigned int grp_id;
@@ -846,6 +862,7 @@ struct scmi_telemetry_de {
 	bool fc_support;
 	bool name_support;
 	struct scmi_telemetry_de_info *info;
+	struct scmi_telemetry_group *grp;
 	bool enabled;
 	bool tstamp_enabled;
 };
@@ -855,6 +872,9 @@ struct scmi_telemetry_res_info {
 	unsigned int num_des;
 	struct scmi_telemetry_de **des;
 	struct scmi_telemetry_de_info *dei_store;
+	unsigned int num_groups;
+	struct scmi_telemetry_group *grps __counted_by_ptr(num_groups);
+	struct scmi_telemetry_grp_info *grps_store;
 };
 
 struct scmi_telemetry_base_info {
-- 
2.54.0




More information about the linux-arm-kernel mailing list