[PATCH v15 23/35] firmware: arm_scmi: Add Telemetry generation counter event

From: Cristian Marussi

Date: Mon Nov 09 2026 - 04:24:51 EST


Add a per-instance generation counter to track configuration changes which
uses the common telemetry events framework.

Signed-off-by: Cristian Marussi <cristian.marussi@xxxxxxx>
---
v14 --> v15
- do NOT reset generation on RESET
v12 --> v13
- update generation counter on do_xfer succcess even if failing to process
response since the change hass been applied: this avoids state misalignment
v11 -->v12
- reviewed generation counter update logic
v6 --> v7
- use new Generic event mechanism based on EventFD instead of WaitQ
v5 --> v6
- add relese semantic to the atomic set of the generation counter
- use new eventfd_ctx common event logic
---
drivers/firmware/arm_scmi/telemetry.c | 46 ++++++++++++++++++++++++++-
include/linux/scmi_protocol.h | 6 ++--
2 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
index ba17204bbfc4..c21700bd1b43 100644
--- a/drivers/firmware/arm_scmi/telemetry.c
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -18,6 +18,7 @@
#include <linux/jhash.h>
#include <linux/io.h>
#include <linux/limits.h>
+#include <linux/lockdep.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -30,6 +31,7 @@
#include <linux/time.h>
#include <linux/uuid.h>
#include <linux/xarray.h>
+#include <linux/wait.h>

#include "errors.h"
#include "protocols.h"
@@ -40,6 +42,7 @@
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000

+#define SCMI_TLM_GENERATION_ONE (SCMI_TLM_GENERATION_INVALID + 1U)
#define SCMI_TLM_TDCF_MAX_RETRIES 15
#define SCMI_TLM_TDCF_RETRY_GAP_US 1234

@@ -821,6 +824,32 @@ scmi_telemetry_tde_cache_lookup(struct telemetry_de *tde,
return 0;
}

+static inline void __scmi_telemetry_generation_set(struct telemetry_info *ti,
+ unsigned int new)
+{
+ int ret;
+
+ atomic_set_release(&ti->info.generation, new);
+
+ ret = scmi_telemetry_event_signal(ti, SCMI_TLM_EVT_GENERATION);
+ if (ret)
+ dev_warn_once(ti->ph->dev,
+ "Could NOT signal telemetry event %d\n",
+ SCMI_TLM_EVT_GENERATION);
+}
+
+static inline void scmi_telemetry_generation_update(struct telemetry_info *ti)
+{
+ unsigned int next;
+
+ lockdep_assert_held(&ti->config_mtx);
+
+ /* Wrap around skipping invalid generation 0 */
+ next = (atomic_read(&ti->info.generation) + 1) ?: SCMI_TLM_GENERATION_ONE;
+
+ __scmi_telemetry_generation_set(ti, next);
+}
+
struct scmi_tlm_de_priv {
struct telemetry_info *ti;
void *next;
@@ -3386,6 +3415,9 @@ scmi_telemetry_remote_state_update_needed(struct telemetry_info *ti,
is_group ? obj : NULL,
!is_group ? obj : NULL);

+ /* A local change can have an impact anyway */
+ scmi_telemetry_generation_update(ti);
+
return false;
}

@@ -3495,6 +3527,8 @@ static int scmi_telemetry_tde_state_set(const struct scmi_protocol_handle *ph,
dev_err(ph->dev,
"Could NOT process DE ID 0x%08X state response - ret:%d\n",
tde->de.info->id, ret);
+
+ scmi_telemetry_generation_update(ti);
}

ph->xops->xfer_put(ph, t);
@@ -3545,6 +3579,8 @@ static int scmi_telemetry_group_state_set(const struct scmi_protocol_handle *ph,
dev_err(ph->dev,
"Could NOT process GROUP %u state response - ret:%d\n",
grp->info->grp_id, ret);
+
+ scmi_telemetry_generation_update(ti);
}

ph->xops->xfer_put(ph, t);
@@ -3705,6 +3741,9 @@ scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph,

ph->xops->xfer_put(ph, t);

+ if (!ret)
+ scmi_telemetry_generation_update(ti);
+
return ret;
}

@@ -4347,8 +4386,11 @@ static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph)

put_unaligned_le32(0, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
- if (!ret)
+ if (!ret) {
scmi_telemetry_local_resources_reset(ti);
+ /* Cumulative generation tick */
+ scmi_telemetry_generation_update(ti);
+ }

ph->xops->xfer_put(ph, t);

@@ -4954,6 +4996,8 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)
INIT_LIST_HEAD(&ti->events[i]);
mutex_init(&ti->events_mtx);

+ /* Generation counter init */
+ atomic_set(&ti->info.generation, SCMI_TLM_GENERATION_ONE);
atomic_set(&ti->des_enabled[ENA_STATE], 0);
atomic_set(&ti->des_enabled[ENA_TSTAMP], 0);
mutex_init(&ti->des_enabled_mtx);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index af882c1e4ea4..585eb3a4684e 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -895,6 +895,7 @@ enum scmi_telemetry_collection {
SCMI_TLM_SINGLE_READ,
};

+#define SCMI_TLM_GENERATION_INVALID 0U
#define SCMI_TLM_GRP_INVALID 0xFFFFFFFF

struct scmi_telemetry_intervals {
@@ -993,6 +994,7 @@ struct scmi_telemetry_info {
uuid_t * __rcu *uuids __counted_by_ptr(num_uuids);
bool enabled;
enum scmi_telemetry_collection current_mode;
+ atomic_t generation;
};

struct scmi_telemetry_de_sample {
@@ -1003,7 +1005,7 @@ struct scmi_telemetry_de_sample {
};

enum scmi_telemetry_event {
- SCMI_TLM_EVT_DUMMY,
+ SCMI_TLM_EVT_GENERATION,
SCMI_TLM_EVT_MAX,
};

@@ -1030,11 +1032,11 @@ enum scmi_telemetry_event {
* the ones belonging to a specific group when provided.
* This causes an immediate update platform-side of all the
* enabled DEs.
- * @reset: reset configuration and telemetry data.
* @event_subscribe: subscribe to the specified event @type using the provided
* @eventfd_ctx.
* @event_unsubscribe: unsubscribe to the specified event @type the previously
* registered @eventfd_ctx.
+ * @reset: reset configuration and telemetry data.
*/
struct scmi_telemetry_proto_ops {
const struct scmi_telemetry_info *(*info_get)
--
2.54.0