[PATCH v14 08/32] firmware: arm_scmi: Add Telemetry resources enumeration

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


Add core Telemetry initialization routines in charge of basic allocations
and DataEvents enumerations.

Signed-off-by: Cristian Marussi <cristian.marussi at arm.com>
---
v13 --> v14
 - splitted out of the old Basic Telemetry patch, contains the basic
   instance allocations and the DEs enumeration process
 - renamed some fields to more strictly adhere to spec nomenclature
---
 drivers/firmware/arm_scmi/telemetry.c | 633 ++++++++++++++++++++++++++
 include/linux/scmi_protocol.h         |  38 ++
 2 files changed, 671 insertions(+)

diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
index 32f6f8d1106d..87ff75805935 100644
--- a/drivers/firmware/arm_scmi/telemetry.c
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -8,15 +8,19 @@
 #include <linux/atomic.h>
 #include <linux/bitfield.h>
 #include <linux/compiler_types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/limits.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/sprintf.h>
 #include <linux/string.h>
 #include <linux/uuid.h>
+#include <linux/xarray.h>
 
 #include "protocols.h"
 #include "notify.h"
@@ -51,13 +55,189 @@ struct scmi_msg_resp_telemetry_protocol_attributes {
 	__le32 default_blk_ts_rate;
 };
 
+struct de_desc_fastchannel {
+	__le32 addr_low;
+	__le32 addr_high;
+	__le32 size;
+};
+
+struct scmi_de_desc {
+	__le32 de_id;
+	__le32 group_id;
+	__le32 de_data_sz;
+	__le32 de_attributes_1;
+#define	IS_NAME_SUPPORTED(d)	(le32_get_bits((d)->de_attributes_1, BIT(31)))
+#define	IS_FC_SUPPORTED(d)	(le32_get_bits((d)->de_attributes_1, BIT(30)))
+#define	GET_DE_TYPE(d)		(le32_get_bits((d)->de_attributes_1, GENMASK(29, 22)))
+#define	IS_PERSISTENT(d)	(le32_get_bits((d)->de_attributes_1, BIT(21)))
+#define GET_DE_UNIT_EXP(d)							\
+	({									\
+		__u32 __signed_exp =						\
+			le32_get_bits((d)->de_attributes_1, GENMASK(20, 13));	\
+										\
+		sign_extend32(__signed_exp, 7);					\
+	})
+#define	GET_DE_UNIT(d)		(le32_get_bits((d)->de_attributes_1, GENMASK(12, 5)))
+#define	TSTAMP_SUPPORT(d)	(le32_get_bits((d)->de_attributes_1, GENMASK(1, 0)))
+	__le32 de_attributes_2;
+#define	GET_DE_INSTA_ID(d)	(le32_get_bits((d)->de_attributes_2, GENMASK(31, 24)))
+#define	GET_COMPO_INSTA_ID(d)	(le32_get_bits((d)->de_attributes_2, GENMASK(23, 8)))
+#define	GET_COMPO_TYPE(d)	(le32_get_bits((d)->de_attributes_2, GENMASK(7, 0)))
+	__le32 de_attributes_3;
+};
+
+struct scmi_msg_resp_telemetry_de_description {
+	__le32 num_desc;
+	struct scmi_de_desc desc[];
+};
+
+enum timestamps {
+	TSTAMP_NONE = 0,
+	TSTAMP_LINE = 1,
+	TSTAMP_BLK = 2,
+};
+
+#define SHMTI_ID_INVALID	GENMASK(31, 0)
+
+struct telemetry_de {
+	enum timestamps ts_type;
+	u32 ts_rate;
+	bool enumerated;
+	void __iomem *base;
+	u32 sid;
+	u32 offset;
+	/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */
+	u32 fc_size;
+	struct list_head item;
+	struct scmi_telemetry_de de;
+	/* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */
+	struct mutex mtx;
+};
+
+#define to_tde(d)	container_of(d, struct telemetry_de, de)
+
 struct telemetry_info {
 	bool streaming_mode;
 	unsigned int num_shmti;
 	unsigned int num_des_tstamp;
 	unsigned int default_blk_ts_rate;
 	const struct scmi_protocol_handle *ph;
+	struct telemetry_de *tdes;
+	struct xarray xa_des;
+	/* Mutex to protect access to @free_des */
+	struct mutex free_mtx;
+	struct list_head free_des;
+	struct list_head fcs_des;
 	struct scmi_telemetry_info info;
+	atomic_t rinfo_initializing;
+	struct completion rinfo_initdone;
+	struct scmi_telemetry_res_info *rinfo;
+	struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti);
+};
+
+static struct scmi_telemetry_res_info *
+__scmi_telemetry_resources_get(struct telemetry_info *ti);
+
+static struct telemetry_de *
+scmi_telemetry_free_tde_get(struct telemetry_info *ti)
+{
+	struct telemetry_de *tde;
+
+	guard(mutex)(&ti->free_mtx);
+
+	tde = list_first_entry_or_null(&ti->free_des, struct telemetry_de, item);
+	if (!tde)
+		return tde;
+
+	list_del(&tde->item);
+
+	return tde;
+}
+
+static void scmi_telemetry_free_tde_put(struct telemetry_info *ti,
+					struct telemetry_de *tde)
+{
+	struct scmi_telemetry_de_info *info;
+
+	lockdep_assert_held(&tde->mtx);
+
+	guard(mutex)(&ti->free_mtx);
+	/* Save clear and restore */
+	info = tde->de.info;
+	memset(info, 0, sizeof(*info));
+	memset(tde, 0, offsetof(struct telemetry_de, mtx));
+	tde->de.info = info;
+	list_add_tail(&tde->item, &ti->free_des);
+}
+
+static struct telemetry_de *scmi_telemetry_tde_lookup(struct telemetry_info *ti,
+						      unsigned int de_id)
+{
+	struct scmi_telemetry_de *de;
+
+	de = xa_load(&ti->xa_des, de_id);
+	if (!de)
+		return NULL;
+
+	return to_tde(de);
+}
+
+static struct telemetry_de *scmi_telemetry_tde_get(struct telemetry_info *ti,
+						   unsigned int de_id)
+{
+	struct telemetry_de *tde;
+
+	/* Pick a new tde */
+	tde = scmi_telemetry_free_tde_get(ti);
+	if (!tde) {
+		dev_err(ti->ph->dev, "Cannot get TDE for ID:0x%08X\n", de_id);
+		return ERR_PTR(-ENOSPC);
+	}
+
+	tde->sid = SHMTI_ID_INVALID;
+
+	return tde;
+}
+
+static int scmi_telemetry_tde_register(struct telemetry_info *ti,
+				       struct telemetry_de *tde)
+{
+	struct scmi_telemetry_res_info *rinfo;
+	int ret;
+
+	lockdep_assert_held(&tde->mtx);
+
+	/* Get rinfo without triggering a recursive enumeration */
+	rinfo = __scmi_telemetry_resources_get(ti);
+
+	if (rinfo->num_des >= ti->info.base.num_des) {
+		ret = -ENOSPC;
+		goto err;
+	}
+
+	/* Store DE pointer by de_id ... */
+	ret = xa_insert(&ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL);
+	if (ret)
+		goto err;
+
+	/* ... and in the general array */
+	rinfo->des[rinfo->num_des] = &tde->de;
+	/* Make sure the freshly registered DE is visible before the index update */
+	smp_store_release(&rinfo->num_des, rinfo->num_des + 1);
+
+	return 0;
+
+err:
+	dev_err(ti->ph->dev, "Cannot register TDE for ID:0x%08X - ret:%d\n",
+		tde->de.info->id, ret);
+
+	return ret;
+}
+
+struct scmi_tlm_de_priv {
+	struct telemetry_info *ti;
+	void *next;
+	size_t rx_len;
 };
 
 static int
@@ -99,6 +279,221 @@ scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti)
 	return ret;
 }
 
+static void iter_tlm_prepare_message(void *message,
+				     unsigned int desc_index, const void *priv)
+{
+	put_unaligned_le32(desc_index, message);
+}
+
+static int iter_de_descr_update_state(struct scmi_iterator_state *st,
+				      const void *response, void *priv)
+{
+	const struct scmi_msg_resp_telemetry_de_description *r = response;
+	struct scmi_tlm_de_priv *p = priv;
+
+	st->num_returned = le32_get_bits(r->num_desc, GENMASK(15, 0));
+	st->num_remaining = le32_get_bits(r->num_desc, GENMASK(31, 16));
+
+	if (st->rx_len < (sizeof(*r) + sizeof(r->desc[0]) * st->num_returned))
+		return -EINVAL;
+
+	/* Initialized to first descriptor and initial payload size */
+	p->next = (void *)r->desc;
+	p->rx_len = st->rx_len - sizeof(*r);
+
+	return 0;
+}
+
+static inline size_t scmi_telemetry_tde_var_size_calc(struct telemetry_de *tde)
+{
+	size_t sz = 0;
+
+	sz += tde->ts_type == TSTAMP_LINE ? sizeof(__le32) : 0;
+	sz += tde->de.fc_support ? sizeof(struct de_desc_fastchannel) : 0;
+	sz += tde->de.name_support ? SCMI_SHORT_NAME_MAX_SIZE : 0;
+
+	return sz;
+}
+
+static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti,
+					      struct telemetry_de *tde,
+					      void **next, size_t *rx_len)
+{
+	const struct scmi_de_desc *desc = *next;
+	size_t payld_sz = sizeof(*desc);
+
+	lockdep_assert_held(&tde->mtx);
+
+	if (*rx_len < payld_sz)
+		return -ENOSPC;
+
+	tde->de.info->id = le32_to_cpu(desc->de_id);
+	tde->de.info->grp_id = le32_to_cpu(desc->group_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);
+	tde->de.info->unit_exp = GET_DE_UNIT_EXP(desc);
+	tde->de.info->instance_id = GET_DE_INSTA_ID(desc);
+	tde->de.info->compo_instance_id = GET_COMPO_INSTA_ID(desc);
+	tde->de.info->compo_type = GET_COMPO_TYPE(desc);
+	tde->de.info->persistent = IS_PERSISTENT(desc);
+	tde->ts_type = TSTAMP_SUPPORT(desc);
+	tde->de.tstamp_support = !!tde->ts_type;
+	tde->de.fc_support = IS_FC_SUPPORTED(desc);
+	tde->de.name_support = IS_NAME_SUPPORTED(desc);
+	/* Update DE_DESCRIPTOR size for the next iteration */
+	*next += sizeof(*desc);
+
+	payld_sz += scmi_telemetry_tde_var_size_calc(tde);
+	if (*rx_len < payld_sz)
+		return -ENOSPC;
+
+	if (tde->ts_type == TSTAMP_LINE) {
+		__le32 *line_ts_rate = *next;
+
+		tde->de.info->ts_rate = le32_to_cpu((*line_ts_rate));
+
+		/* Variably sized depending on TS support */
+		*next += sizeof(*line_ts_rate);
+	} else if (tde->ts_type == TSTAMP_BLK) {
+		/* Setup default BLK TS value at first */
+		tde->de.info->ts_rate = ti->default_blk_ts_rate;
+	}
+
+	if (tde->de.fc_support) {
+		u32 size;
+		u64 phys_addr;
+		void __iomem *addr;
+		struct de_desc_fastchannel *dfc;
+
+		dfc = *next;
+		phys_addr = le32_to_cpu(dfc->addr_low);
+		phys_addr |= (u64)le32_to_cpu(dfc->addr_high) << 32;
+
+		size = le32_to_cpu(dfc->size);
+		addr = devm_ioremap(ti->ph->dev, phys_addr, size);
+		if (!addr)
+			return -EADDRNOTAVAIL;
+
+		tde->base = addr;
+		tde->offset = 0;
+		tde->fc_size = size;
+
+		/* Variably sized depending on FC support */
+		*next += sizeof(*dfc);
+	}
+
+	if (tde->de.name_support) {
+		const char *de_name = *next;
+
+		strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE);
+		/* Variably sized depending on name support */
+		*next += SCMI_SHORT_NAME_MAX_SIZE;
+	}
+
+	/* Count timestamped DEs */
+	ti->num_des_tstamp += !!tde->de.tstamp_support;
+
+	*rx_len -= payld_sz;
+
+	return 0;
+}
+
+static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,
+					  const void *response,
+					  struct scmi_iterator_state *st,
+					  void *priv)
+{
+	struct scmi_tlm_de_priv *p = priv;
+	struct telemetry_info *ti = p->ti;
+	const struct scmi_de_desc *desc = p->next;
+	struct telemetry_de *tde;
+	bool discovered = false;
+	unsigned int de_id;
+	int ret;
+
+	de_id = le32_to_cpu(desc->de_id);
+	/* Check if this DE has already been discovered by other means... */
+	tde = scmi_telemetry_tde_lookup(ti, de_id);
+	if (!tde) {
+		/* Create a new one */
+		tde = scmi_telemetry_tde_get(ti, de_id);
+		if (IS_ERR(tde))
+			return PTR_ERR(tde);
+
+		discovered = true;
+	}
+
+	guard(mutex)(&tde->mtx);
+	if (tde->enumerated) {
+		/* Cannot be a duplicate of a DE already created by enumeration */
+		dev_err(ph->dev,
+			"Discovered INVALID DE with DUPLICATED ID:0x%08X\n",
+			de_id);
+		return -EINVAL;
+	}
+
+	ret = scmi_telemetry_de_descriptor_parse(ti, tde, &p->next, &p->rx_len);
+	if (ret) {
+		dev_err(ph->dev, "Malformed DE Descriptor - ret:%d\n", ret);
+		/* DE not enumerated at this point were created in this call */
+		if (discovered)
+			scmi_telemetry_free_tde_put(ti, tde);
+		return ret;
+	}
+
+	/* Now fully enumerated */
+	tde->enumerated = true;
+
+	if (discovered) {
+		/* Register if it was not already ... */
+		ret = scmi_telemetry_tde_register(ti, tde);
+		if (ret) {
+			/* DE not enumerated at this point were created in this call */
+			scmi_telemetry_free_tde_put(ti, tde);
+			return ret;
+		}
+	}
+
+	/* Add to FastChannels list */
+	if (tde->de.fc_support)
+		list_add(&tde->item, &ti->fcs_des);
+
+	return 0;
+}
+
+static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti)
+{
+	const struct scmi_protocol_handle *ph = ti->ph;
+
+	struct scmi_iterator_ops ops = {
+		.prepare_message = iter_tlm_prepare_message,
+		.update_state = iter_de_descr_update_state,
+		.process_response = iter_de_descr_process_response,
+	};
+	struct scmi_tlm_de_priv tpriv = {
+		.ti = ti,
+		.next = NULL,
+	};
+	void *iter;
+	int ret;
+
+	if (!ti->info.base.num_des)
+		return 0;
+
+	iter = ph->hops->iter_response_init(ph, &ops, ti->info.base.num_des,
+					    TELEMETRY_DE_DESCRIPTION,
+					    sizeof(u32), &tpriv);
+	if (IS_ERR(iter))
+		return PTR_ERR(iter);
+
+	ret = ph->hops->iter_response_run(iter);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static const struct scmi_telemetry_info *
 scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)
 {
@@ -107,10 +502,242 @@ scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)
 	return &ti->info;
 }
 
+static const struct scmi_telemetry_de *
+scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	ti->res_get(ti);
+	return xa_load(&ti->xa_des, id);
+}
+
+static const struct scmi_telemetry_res_info *
+scmi_telemetry_resources_get(const struct scmi_protocol_handle *ph)
+{
+	struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti);
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	/* Ensure the latest installed res_get helper is visible */
+	res_get = smp_load_acquire(&ti->res_get);
+
+	return res_get(ti);
+}
+
 static const struct scmi_telemetry_proto_ops tlm_proto_ops = {
 	.info_get = scmi_telemetry_info_get,
+	.de_lookup = scmi_telemetry_de_lookup,
+	.res_get = scmi_telemetry_resources_get,
 };
 
+/**
+ * scmi_telemetry_resources_alloc  - Resources allocation
+ * @ti: A reference to the telemetry info descriptor for this instance
+ *
+ * This allocates and initializes dedicated resources for the maximum possible
+ * number of needed telemetry resources, based on information gathered from
+ * the initial enumeration: these allocations represent an upper bound on
+ * the number of discoverable telemetry resources and they will be later
+ * populated during late deferred further discovery phases.
+ *
+ * Return: 0 on Success, errno otherwise
+ */
+static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)
+{
+	/* Array to hold pointers to discovered DEs */
+	struct scmi_telemetry_de **des __free(kfree) =
+		kzalloc_objs(*des, ti->info.base.num_des);
+	if (!des)
+		return -ENOMEM;
+
+	/* The allocated DE descriptors */
+	struct telemetry_de *tdes __free(kfree) =
+		kzalloc_objs(*tdes, ti->info.base.num_des);
+	if (!tdes)
+		return -ENOMEM;
+
+	/* Allocate a set of contiguous DE info descriptors. */
+	struct scmi_telemetry_de_info *dei_store __free(kfree) =
+		kzalloc_objs(*dei_store, ti->info.base.num_des);
+	if (!dei_store)
+		return -ENOMEM;
+
+	struct scmi_telemetry_res_info *rinfo __free(kfree) =
+		kzalloc_obj(*rinfo);
+	if (!rinfo)
+		return -ENOMEM;
+
+	mutex_init(&ti->free_mtx);
+	INIT_LIST_HEAD(&ti->free_des);
+	for (int i = 0; i < ti->info.base.num_des; i++) {
+		mutex_init(&tdes[i].mtx);
+		guard(mutex)(&tdes[i].mtx);
+
+		/* Bind contiguous DE info structures */
+		tdes[i].de.info = &dei_store[i];
+		scmi_telemetry_free_tde_put(ti, &tdes[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);
+
+	/* Ensure all of the above assignments are visible */
+	smp_store_release(&ti->rinfo, no_free_ptr(rinfo));
+
+	return 0;
+}
+
+static struct scmi_telemetry_res_info *
+__scmi_telemetry_resources_get(struct telemetry_info *ti)
+{
+	/* Ensure rinfo descriptor is visible */
+	return smp_load_acquire(&ti->rinfo);
+}
+
+static void scmi_telemetry_resources_free(struct telemetry_info *ti)
+{
+	struct scmi_telemetry_res_info *rinfo;
+
+	/* Get rinfo without triggering a recursive enumeration, cannot be NULL */
+	rinfo = __scmi_telemetry_resources_get(ti);
+	/* Ensure rinfo is no more accessible upfront */
+	smp_store_release(&ti->rinfo, NULL);
+
+	kfree(ti->tdes);
+
+	kfree(rinfo->des);
+	kfree(rinfo->dei_store);
+
+	kfree(rinfo);
+
+	dev_dbg(ti->ph->dev, "SCMI Telemetry resources freed for instance\n");
+}
+
+static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)
+{
+	struct scmi_telemetry_de *de;
+	unsigned long idx;
+
+	xa_for_each(&ti->xa_des, idx, de) {
+		struct telemetry_de *tde = to_tde(de);
+
+		guard(mutex)(&tde->mtx);
+
+		scmi_telemetry_free_tde_put(ti, tde);
+	}
+
+	xa_destroy(&ti->xa_des);
+}
+
+static void scmi_telemetry_instance_cleanup(void *arg)
+{
+	struct telemetry_info *ti = arg;
+
+	scmi_telemetry_tdes_cleanup(ti);
+	scmi_telemetry_resources_free(ti);
+}
+
+/**
+ * scmi_telemetry_resources_enumerate  - Enumeration helper
+ * @ti: A reference to the telemetry info descriptor for this instance
+ *
+ * This helper is configured to be called once on the first enumeration
+ * attempt, when triggered by invoking ti->res_get() from somewhere else.
+ *
+ * Once run it substitues itself in ti->res_get() with the simple accessor
+ * __scmi_telemetry_resources_get, which returns a descriptor to the resources
+ * that were possibly discovered.
+ *
+ * Note that, while it attempts to fully enumerate Data Events and Groups, it
+ * does NOT fail when such enumerations fail, instead it simply gives up with
+ * the end result that only a partially populated, but consistent, resources
+ * descriptor will be returned; in such a case the incomplete descriptor will
+ * be marked as NOT fully_enumerated: this design enables the kernel to deal
+ * with badly implemented out-of-spec firmware support while keep on providing
+ * a minimal sane, albeit possibly incomplete, set of telemetry respources.
+ *
+ * Return: A reference to a fully or partially populated resources descriptor
+ */
+static struct scmi_telemetry_res_info *
+scmi_telemetry_resources_enumerate(struct telemetry_info *ti)
+{
+	struct device *dev = ti->ph->dev;
+	int ret;
+
+	/*
+	 * Ensure the following initialization can be called only once
+	 * from one thread of execution.
+	 */
+	if (atomic_cmpxchg(&ti->rinfo_initializing, 0, 1)) {
+		/*
+		 * When initialization is already ongoing in another thread,
+		 * just wait for its completion and return the fully or partially
+		 * populated rinfo.
+		 */
+		if (!completion_done(&ti->rinfo_initdone))
+			wait_for_completion(&ti->rinfo_initdone);
+
+		/* Ensure rinfo descriptor is visible */
+		return smp_load_acquire(&ti->rinfo);
+	}
+
+	/* Note that this code below can be run only once by one thread */
+	ret = scmi_telemetry_de_descriptors_get(ti);
+	if (ret) {
+		dev_err(dev, FW_BUG "Cannot fully enumerate DEs resources. Degraded system.\n");
+		goto done;
+	}
+
+	ti->rinfo->fully_enumerated = true;
+done:
+	/* Disable initialization permanently */
+	smp_store_release(&ti->res_get, __scmi_telemetry_resources_get);
+
+	/* Unblock concurrent threads that have been stalled */
+	complete_all(&ti->rinfo_initdone);
+
+	/* Ensure local rinfo is visible before returning it */
+	smp_mb();
+	return ti->rinfo;
+}
+
+/**
+ * scmi_telemetry_instance_init  - Instance initializer
+ * @ti: A reference to the telemetry info descriptor for this instance
+ *
+ * Note that this allocates and initialize all the resources possibly needed
+ * and then setups the @scmi_telemetry_resources_enumerate helper as the
+ * default method for the first call to ti->res_get(): this mechanism enables
+ * the possibility of optionally implementing deferred enumeration policies
+ * which optionally delay the discovery phase and related SCMI message exchanges
+ * to a later point in time.
+ *
+ * Return: 0 on Success, errno otherwise
+ */
+static int scmi_telemetry_instance_init(struct telemetry_info *ti)
+{
+	int ret;
+
+	/* Allocate and Initialize on first call... */
+	ret = scmi_telemetry_resources_alloc(ti);
+	if (ret)
+		return ret;
+
+	xa_init(&ti->xa_des);
+
+	/* Setup resources lazy initialization */
+	atomic_set(&ti->rinfo_initializing, 0);
+	init_completion(&ti->rinfo_initdone);
+	/* Ensure the new res_get() operation is visible after this point */
+	smp_store_mb(ti->res_get, scmi_telemetry_resources_enumerate);
+
+	return devm_add_action_or_reset(ti->ph->dev,
+					scmi_telemetry_instance_cleanup, ti);
+}
+
 static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)
 {
 	struct device *dev = ph->dev;
@@ -132,6 +759,12 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)
 		return ret;
 	}
 
+	ret = scmi_telemetry_instance_init(ti);
+	if (ret) {
+		dev_err(dev, "Cannot initialize instance. Abort.\n");
+		return ret;
+	}
+
 	ti->info.base.version = ph->version;
 
 	return ph->set_priv(ph, ti);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 121c83e1c6d8..018427daf55f 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -13,6 +13,7 @@
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/types.h>
+#include <linux/uuid.h>
 
 #define SCMI_MAX_STR_SIZE		64
 #define SCMI_SHORT_NAME_MAX_SIZE	16
@@ -825,6 +826,37 @@ struct scmi_pinctrl_proto_ops {
 	int (*pin_free)(const struct scmi_protocol_handle *ph, u32 pin);
 };
 
+struct scmi_telemetry_de_info {
+	unsigned int id;
+	unsigned int grp_id;
+	unsigned int data_sz;
+	unsigned int type;
+	unsigned int unit;
+	int unit_exp;
+	unsigned int ts_rate;
+	unsigned int instance_id;
+	unsigned int compo_instance_id;
+	unsigned int compo_type;
+	bool persistent;
+	char name[16];
+};
+
+struct scmi_telemetry_de {
+	bool tstamp_support;
+	bool fc_support;
+	bool name_support;
+	struct scmi_telemetry_de_info *info;
+	bool enabled;
+	bool tstamp_enabled;
+};
+
+struct scmi_telemetry_res_info {
+	bool fully_enumerated;
+	unsigned int num_des;
+	struct scmi_telemetry_de **des;
+	struct scmi_telemetry_de_info *dei_store;
+};
+
 struct scmi_telemetry_base_info {
 	unsigned int version;
 	uuid_t primary_revision;
@@ -848,10 +880,16 @@ struct scmi_telemetry_info {
  *	by SCMI Telemetry Protocol
  *
  * @info_get: get the general Telemetry information.
+ * @de_lookup: get a specific DE descriptor from the DE id.
+ * @res_get: get a reference to the Telemetry resources descriptor.
  */
 struct scmi_telemetry_proto_ops {
 	const struct scmi_telemetry_info __must_check *(*info_get)
 		(const struct scmi_protocol_handle *ph);
+	const struct scmi_telemetry_de __must_check *(*de_lookup)
+		(const struct scmi_protocol_handle *ph, u32 id);
+	const struct scmi_telemetry_res_info __must_check *(*res_get)
+		(const struct scmi_protocol_handle *ph);
 };
 
 /**
-- 
2.54.0




More information about the linux-arm-kernel mailing list