[PATCH v7 14/23] firmware: arm_scmi: Add Telemetry generation counter event

From: Cristian Marussi

Date: Sun Aug 02 2026 - 11:04:35 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>
---
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 | 48 +++++++++++++++++++++++++++
include/linux/scmi_protocol.h | 5 ++-
2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
index dfe18729c6f1..8cb81afd7781 100644
--- a/drivers/firmware/arm_scmi/telemetry.c
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -23,6 +23,7 @@
#include <linux/string.h>
#include <linux/uuid.h>
#include <linux/xarray.h>
+#include <linux/wait.h>

#include "protocols.h"
#include "notify.h"
@@ -32,6 +33,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 5

#define SCMI_TLM_DE_IMPL_NUM_DWORDS 4
@@ -700,6 +702,35 @@ 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;
+
+ /* Wrap around skipping invalid generation 0 */
+ next = (atomic_read(&ti->info.generation) + 1) ?: SCMI_TLM_GENERATION_ONE;
+
+ __scmi_telemetry_generation_set(ti, next);
+}
+
+static inline void scmi_telemetry_generation_reset(struct telemetry_info *ti)
+{
+ __scmi_telemetry_generation_set(ti, SCMI_TLM_GENERATION_ONE);
+}
+
struct scmi_tlm_de_priv {
struct telemetry_info *ti;
void *next;
@@ -2351,6 +2382,8 @@ static int __scmi_telemetry_state_set(const struct scmi_protocol_handle *ph,
tstamp_enabled_state,
*tstamp);

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

@@ -2412,6 +2445,9 @@ static int __scmi_telemetry_state_set(const struct scmi_protocol_handle *ph,

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

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

@@ -2544,6 +2580,9 @@ static int scmi_telemetry_all_disable(const struct scmi_protocol_handle *ph,

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

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

@@ -2616,6 +2655,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;
}

@@ -3080,6 +3122,10 @@ static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph)
struct telemetry_info *ti = ph->get_priv(ph);

scmi_telemetry_local_resources_reset(ti);
+
+ /* Reset generation now that server has been reset */
+ scmi_telemetry_generation_reset(ti);
+
/* Fetch again the states from platform. */
ret = scmi_telemetry_initial_state_lookup(ti);
if (ret)
@@ -3504,6 +3550,8 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)
for (int i = 0; i < SCMI_TLM_EVT_MAX; i++)
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);
/* Setup resources lazy initialization */
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 3206b0710bcc..d48b1030ddb4 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -891,6 +891,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 {
@@ -986,6 +987,7 @@ struct scmi_telemetry_info {
bool enabled;
bool notif_enabled;
enum scmi_telemetry_collection current_mode;
+ atomic_t generation;
};

struct scmi_telemetry_de_sample {
@@ -996,6 +998,7 @@ struct scmi_telemetry_de_sample {
};

enum scmi_telemetry_event {
+ SCMI_TLM_EVT_GENERATION,
SCMI_TLM_EVT_MAX
};

@@ -1022,11 +1025,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 __must_check *(*info_get)
--
2.54.0