linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Introduce SCMI Telemetry support
@ 2025-09-25 20:35 Cristian Marussi
  2025-09-25 20:35 ` [PATCH 01/10] firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value Cristian Marussi
                   ` (9 more replies)
  0 siblings, 10 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi


Hi all,

the upcoming SCMI v4.0 specification [0] introduces a new SCMI protocol
dedicated to System Telemetry.

In a nutshell, the SCMI Telemetry protocol allows an agent to discover at
runtime the set of Telemetry Data Events (DEs) available on a specific
platform and provides the means to configure the set of DEs that a user is
interested into, while read them back using the collection method that is
deeemd more suitable for the usecase at hand.

Without delving into the gory details of the whole SCMI Telemetry protocol
let's just say that the SCMI platform firmware advertises a number of
Telemetry Data Events, each one identified by a 32bit unique ID, and a user
can read back at will the associated data value in a number of ways.

Anyway, the set of well-known architected DE IDs defined by the spec is
limited to a dozen IDs, which means that the vast majority of DE IDs are
customizable per-platform: as a consequence the same ID, say '0x1234',
could represent completely different things on different systems.

Data Event IDs definitions and their semantic are supposed to be described
using some sort of JSON-like description file consumed by a userspace tool,
which would be finally in charge of making sense of the exact meaning of
the set of DEs specifically defined as available on a specific platform.

IOW, in turn, this means that even though the DEs enumerated via SCMI come
with some sort of topological and qualitative description (like unit of
measurements), kernel-wise we CANNOT be sure of "what is what" without
being fed-back some sort of information about the DEs semantic by the afore
mentioned userspace tool.

For these reasons, currently this series does NOT attempt to register
any of the discovered DEs with any of the usual in-kernel subsystems (like
HWMON, IIO, POWERCAP, PERF etc), simply because we cannot be sure if a DE
is suitable or not for a given subsystem. This also means there are NO
in-kernel user of these Telemetry data, as of now.

So, while we do not exclude, for the future, to feed/register some of the
discovered DEs to some of the above mentioned Kernel subsystems, as of
now we have ONLY modeled a custom userspace API to make SCMI Telemetry
available to userspace users.

In deciding which kind of interface to expose SCMI Telemetry data to a
user, this new SCMI Telemetry driver aims at satisfying 2 main reqs:

 - exposing an FS-based human-readable interface that can be used to
   discover, configure and access our Telemetry data directly from the
   shell

 - exposing an alternative machine-friendly, more-performant, binary
   interface that can be used to avoid the overhead of multiple accesses
   to the VFS and that can be more suitable to access with a custom tool

In the previous RFC posted a few months ago [1], the above was achieved
with a combination of a SysFS interface, for the human-readable side of
the story, and a classic chardev/ioctl for the plain binary access.

In this V1, instead, I moved away from this combined approach, especially
away from SysFS, for the following reason:

 1. "Abusing SysFS": SysFS is a handy way to expose device related
      properties in a common way, using a few common helpers built on
      kernfs; this means, though, that unfortunately in our scenario I had
      to generate a dummy simple device for EACH SCMI Telemetry DataEvent
      that I got to discover at runtime and attach to them, all of the
      properties I need.
      This by itself seemed to me abusing the SysFS framework, but even
      ignoring this, the impact on the system when we have to deal with
      hundreds or tens of thousands od DEs is sensible.
      In some test scenario I ended with 50k devices and half-a-millon
      related property files ... O_o

 2. "SysFS constraints": SysFS usage itself has its well-known constraints
      and best practices, like the one-file/one-value rule, and due to the
      fact that any virtual file with a complex structure or handling logic
      is frowned upon, you can forget about IOTCLs and mmap'ing to provide
      a more performant interface, which is the reason why, in the previous
      RFC, there was an additional alternative chardev interface.

 2. "Chardev limitations": Given the nature of the protocol the character
      devices approach itself was problematic: first of all because there
      is an upper limit on the number of chardev we can create, dictated by
      the range of available minor numbers and then because the fact
      itself to have to maintain 2 completely different interfaces is a
      pain.

As a final remark, please note that all of this is supposed to be available
in production systems across a number of heterogeneous platforms: for these
reasons the easy choice, debugFS, is not an option.

Due to the above reasoning, in V1 we opted for a new approach, with the
proposed interfaces now based on a full fledged, unified, virtual
filesystem implemented from scratch so that we can:

 - expose all the DEs property we like as before with SysFS, but without
   any of the constraint imposed by the usage of SysFs or kernfs.

 - additionally expose a few alternative and more performant interfaces
   by embedding in that same FS, a few special virtual files:

   + 'control': to issue IOCTLs for quicker binary discovery and on-demand
   		access to data
   + 'pipe' [TBD]: to provide a stream of events using a virtual
		   infinite-style file
   + 'raw_<N>' [TBD]: to provide direct memory mapped access to the raw
   		      SCMI Telemetry data


INTERFACES
===========

We propose a couple of interfaces, both rooted in the same unified
TelemetryFS, which can be mounted with:

	mount -t stlmfs none /sys/fs/arm_telemetry/

 1. a FS based human-readable API tree

   This API present the discovered DEs and DEs-groups rooted under a
   structrure like this:

	/sys/fs/arm_telemetry/tlm_0/
	|-- all_des_enable
	|-- all_des_tstamp_enable
	|-- available_update_intervals_ms
	|-- current_update_interval_ms
	|-- de_implementation_version
	|-- des
	|   |-- 0x0000
	|   |-- 0x0016
	|   |-- 0x1010
	|   |-- 0xA000
	|   |-- 0xA001
	|   |-- 0xA002
	|   |-- 0xA005
	|   |-- 0xA007
	|   |-- 0xA008
	|   |-- 0xA00A
	|   |-- 0xA00B
	|   |-- 0xA00C
	|   `-- 0xA010
	|-- des_bulk_read
	|-- des_single_sample_read
	|-- groups
	|   |-- 0
	|   `-- 1
	|-- intervals_discrete
	|-- reset
	|-- tlm_enable
	`-- version

	At the top level we have general configuration knobs to:

	- enable/disable all DEs with or without tstamp
	- configure the update interval that the platform will use
	- enable Telemetry as a whole oe rest the whole stack
	- read all the enabled DEs in a buffer one-per-line
		<DE_ID> <TIMESTAMP> <DATA_VALUE>
	- des_single_sample_read to request an immediate updated read of
	  all the enabled DEs in a single buffer one-per-line:
		<DE_ID> <TIMESTAMP> <DATA_VALUE>
        
	where each DE in turn is represented by a flat subtree like:

	tlm_0/des/0xA001/
	|-- compo_instance_id
	|-- compo_type
	|-- enable
	|-- instance_id
	|-- name
	|-- persistent
	|-- tstamp_enable
	|-- tstamp_exp
	|-- type
	|-- unit
	|-- unit_exp
	`-- value

	where, beside a bunch of description items, you can:

	- enable/disable a single DE
	- read back its tstamp and data from 'value' as in:
		<TIMESTAMP>: <DATA_VALUE>

	then for each discovered group of DEs:

	scmi_tlm_0/groups/0/
	|-- available_update_intervals_ms
	|-- composing_des
	|-- current_update_interval_ms
	|-- des_bulk_read
	|-- des_single_sample_read
	|-- enable
	|-- intervals_discrete
	`-- tstamp_enable

	you can find the knobs to:
	
	- enable/disable the group as a whole
	- lookup group composition
	- set a per-group update interval (if supported)
	- des_bulk_read to read all the enabled DEs for this group in a
	  single buffer one-per-line:
		<DE_ID> <TIMESTAMP> <DATA_VALUE>
	- des_single_sample_read to request an immediate updated read of
	  all the enabled DEs for this group in a single buffer
	  one-per-line:
		<DE_ID> <TIMESTAMP> <DATA_VALUE>

 2. Leveraging the capabilities of a full-fledged filesystem and the
    topological information provided by SCMI Telemetry we expose also
    and alternative view of the above tree, by hardlinking a few of the
    same entries above under another, topologically sorted, subtree:

    components/
    |-- cpu
    |   |-- 0
    |   |   |-- celsius
    |   |   |   `-- 0
    |   |   |       `-- 0x0001
    |   |   |           |-- compo_instance_id
    |   |   |           |-- compo_type
    |   |   |           |-- enable
    |   |   |           |-- instance_id
    |   |   |           |-- name
    |   |   |           |-- persistent
    |   |   |           |-- tstamp_enable
    |   |   |           |-- tstamp_exp
    |   |   |           |-- type
    |   |   |           |-- unit
    |   |   |           |-- unit_exp
    |   |   |           `-- value
    |   |   `-- cycles
    |   |       |-- 0
    |   |       |   `-- 0x1010
    |   |	    |    ....
	.................
	...............
    |   |-- 1
    |   |   `-- celsius
    |   |       `-- 0
    |   |           `-- 0x0002
    |   |           .........
    |   `-- 2
    |       `-- celsius
    |           `-- 0
    |               `-- 0x0003
    |-- interconnnect
    |   `-- 0
    |       `-- hertz
    |           `-- 0
    |               |-- 0xA008
    |               `-- 0xA00B
    |-- mem_cntrl
    |   `-- 0
    |       |-- bps
    |       |   `-- 0
    |       |       `-- 0xA00A
    |       |-- celsius
    |       |   `-- 0
    |       |       `-- 0xA007
    |       `-- joules
    |           `-- 0
    |               `-- 0xA002
    |-- periph
    |   |-- 0
    |   |   `-- messages
    |   |       `-- 0
    |   |           `-- 0x0016
    |   |-- 1
    |   |   `-- messages
    |   |       `-- 0
    |   |           `-- 0x0017
    |   `-- 2
    |       `-- messages
    |           `-- 0
    |               `-- 0x0018
    `-- unspec
    `-- 0
    |-- celsius
    |   `-- 0
    |       `-- 0xA005


   ...so as to provide the human user with a more understandable layout.


All of this is nice and fancy human-readable, easily scriptable, but
certainly not the fastest possible to access especially on huge trees...

 ... so for the afore-mentioned reasons we alternatively expose

 3. a more performant API based on IOCTLs as described fully in:

	include/uapi/linux/scmi.h

   As described succinctly in the above UAPI header too, this API is meant
   to be called on a few special files named 'control' that are populated
   into the tree:

   .
   |-- all_des_enable
   .....
   |-- components
   |   |-- cpu
   |   |-- interconnnect
   |   |-- mem_cntrl
   |   |-- periph
   |   `-- unspec
   |-- control
   .....................

   |-- groups
   |   |-- 0
   |   |   |-- available_update_intervals_ms
   |   |   |-- composing_des
   |   |   |-- control
   .....................
   |   |-- 1
   |   |   |-- available_update_intervals_ms
   |   |   |-- composing_des
   |   |   |-- control
   .....................
   |   `-- 2
   |       |-- available_update_intervals_ms
   |       |-- composing_des
   |       |-- control
   .....................

  This will allow a tool to:

   - use some IOCTLs to configure a set of properties equivalent to the
     ones above in FS
   - use some other IOCTLs for direct access to data in binary format
     for a single DEs or all of them


Missing feats & next steps
--------------------------
 - FS internals cleanup and testing
 - enforce a single mount
 - handle platform configured with boot_on Telemetry
 - add streaming mode interface via 'pipe' file
 - add direct access interface via mmap-able 'raw' file
 - provide a simple test tool to make use special file access via
   ioctls/mmap etc...
 - more tests on multiple SCMI instances scenario


Known Issues
------------
 - missing proper docs for the FS human-readable interface
 - FS uses legacy mount implementation...use fs-context
 - FS corruption and troubles on unmount
 - needs a better handling of races between multiple probes and
   concurrent mount

Thise series now support SCMI Telemetry ALPHA_0 specification in all of
its features. (with bugs probably still... :P)

Based on V6.17-rc6, tested on an emulated setup.

Any feedback welcome,

Thanks,
Cristian

----

RFC-> V1
---
- moved from SysFS/chardev to a full fledged FS
- added support for SCMI Telemetry BLK timestamps


Thanks,
Cristian

[0]: https://developer.arm.com/documentation/den0056/f/?lang=en
[1]: https://lore.kernel.org/arm-scmi/20250620192813.2463367-1-cristian.marussi@arm.com/

Cristian Marussi (10):
  firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value
  firmware: arm_scmi: Reduce the scope of protocols mutex
  firmware: arm_scmi: Allow protocols to register for notifications
  uapi: Add ARM SCMI definitions
  firmware: arm_scmi: Add Telemetry protocol support
  firmware: arm_scmi: Add System Telemetry driver
  firmware: arm_scmi: Add System Telemetry ioctls support
  firmware: arm_scmi: Add Telemetry components view
  include: trace: Add Telemetry trace events
  firmware: arm_scmi: Use new Telemetry traces

 MAINTAINERS                                   |    1 +
 drivers/firmware/arm_scmi/Kconfig             |   10 +
 drivers/firmware/arm_scmi/Makefile            |    3 +-
 drivers/firmware/arm_scmi/common.h            |    4 +
 drivers/firmware/arm_scmi/driver.c            |   67 +-
 drivers/firmware/arm_scmi/notify.c            |   31 +-
 drivers/firmware/arm_scmi/notify.h            |    8 +-
 drivers/firmware/arm_scmi/protocols.h         |    9 +
 .../firmware/arm_scmi/scmi_system_telemetry.c | 2496 +++++++++++++++++
 drivers/firmware/arm_scmi/telemetry.c         | 2156 ++++++++++++++
 include/linux/scmi_protocol.h                 |  188 +-
 include/trace/events/scmi.h                   |   48 +-
 include/uapi/linux/scmi.h                     |  286 ++
 13 files changed, 5265 insertions(+), 42 deletions(-)
 create mode 100644 drivers/firmware/arm_scmi/scmi_system_telemetry.c
 create mode 100644 drivers/firmware/arm_scmi/telemetry.c
 create mode 100644 include/uapi/linux/scmi.h

-- 
2.51.0


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 01/10] firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-25 20:35 ` [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex Cristian Marussi
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add a common definition of SCMI_MAX_PROTOCOLS and use it all over the
SCMI stack.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/notify.c | 4 +---
 include/linux/scmi_protocol.h      | 3 +++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index dee9f238f6fd..78e9e27dc9ec 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -94,8 +94,6 @@
 #include "common.h"
 #include "notify.h"
 
-#define SCMI_MAX_PROTO		256
-
 #define PROTO_ID_MASK		GENMASK(31, 24)
 #define EVT_ID_MASK		GENMASK(23, 16)
 #define SRC_ID_MASK		GENMASK(15, 0)
@@ -1673,7 +1671,7 @@ int scmi_notification_init(struct scmi_handle *handle)
 	ni->gid = gid;
 	ni->handle = handle;
 
-	ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO,
+	ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTOCOLS,
 						sizeof(char *), GFP_KERNEL);
 	if (!ni->registered_protocols)
 		goto err;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 688466a0e816..59527193d6dd 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -926,8 +926,11 @@ enum scmi_std_protocol {
 	SCMI_PROTOCOL_VOLTAGE = 0x17,
 	SCMI_PROTOCOL_POWERCAP = 0x18,
 	SCMI_PROTOCOL_PINCTRL = 0x19,
+	SCMI_PROTOCOL_LAST = 0x7f,
 };
 
+#define SCMI_MAX_PROTOCOLS	256
+
 enum scmi_system_events {
 	SCMI_SYSTEM_SHUTDOWN,
 	SCMI_SYSTEM_COLDRESET,
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
  2025-09-25 20:35 ` [PATCH 01/10] firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-10-17 15:07   ` Jonathan Cameron
  2025-09-25 20:35 ` [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications Cristian Marussi
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Currently the mutex dedicated to the protection of the list of registered
protocols is held during all the protocol initialization phase.

Such a wide locking region is not needed and causes problem when trying to
initialize notifications from within a protocol initialization routine.

Reduce the scope of the protocol mutex.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 53 +++++++++++++++---------------
 1 file changed, 26 insertions(+), 27 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index bd56a877fdfc..71ee25b78624 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -17,6 +17,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/bitmap.h>
+#include <linux/cleanup.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/export.h>
@@ -2179,10 +2180,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
 	if (ret)
 		goto clean;
 
-	ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
-			GFP_KERNEL);
-	if (ret != proto->id)
-		goto clean;
+	/* Finally register the initialized protocol */
+	scoped_guard(mutex, &info->protocols_mtx) {
+		ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
+				GFP_KERNEL);
+		if (ret != proto->id)
+			goto clean;
+	}
 
 	/*
 	 * Warn but ignore events registration errors since we do not want
@@ -2243,25 +2247,22 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
 static struct scmi_protocol_instance * __must_check
 scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
 {
-	struct scmi_protocol_instance *pi;
+	struct scmi_protocol_instance *pi = ERR_PTR(-EPROBE_DEFER);
 	struct scmi_info *info = handle_to_scmi_info(handle);
+	const struct scmi_protocol *proto;
 
-	mutex_lock(&info->protocols_mtx);
-	pi = idr_find(&info->protocols, protocol_id);
-
-	if (pi) {
-		refcount_inc(&pi->users);
-	} else {
-		const struct scmi_protocol *proto;
-
-		/* Fails if protocol not registered on bus */
-		proto = scmi_protocol_get(protocol_id, &info->version);
-		if (proto)
-			pi = scmi_alloc_init_protocol_instance(info, proto);
-		else
-			pi = ERR_PTR(-EPROBE_DEFER);
+	scoped_guard(mutex, &info->protocols_mtx) {
+		pi = idr_find(&info->protocols, protocol_id);
+		if (pi) {
+			refcount_inc(&pi->users);
+			return pi;
+		}
 	}
-	mutex_unlock(&info->protocols_mtx);
+
+	/* Fails if protocol not registered on bus */
+	proto = scmi_protocol_get(protocol_id, &info->version);
+	if (proto)
+		pi = scmi_alloc_init_protocol_instance(info, proto);
 
 	return pi;
 }
@@ -2294,10 +2295,11 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
 	struct scmi_info *info = handle_to_scmi_info(handle);
 	struct scmi_protocol_instance *pi;
 
-	mutex_lock(&info->protocols_mtx);
-	pi = idr_find(&info->protocols, protocol_id);
-	if (WARN_ON(!pi))
-		goto out;
+	scoped_guard(mutex, &info->protocols_mtx) {
+		pi = idr_find(&info->protocols, protocol_id);
+		if (WARN_ON(!pi))
+			return;
+	}
 
 	if (refcount_dec_and_test(&pi->users)) {
 		void *gid = pi->gid;
@@ -2316,9 +2318,6 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
 		dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
 			protocol_id);
 	}
-
-out:
-	mutex_unlock(&info->protocols_mtx);
 }
 
 void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
  2025-09-25 20:35 ` [PATCH 01/10] firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value Cristian Marussi
  2025-09-25 20:35 ` [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-10-17 15:10   ` Jonathan Cameron
  2025-09-25 20:35 ` [PATCH 04/10] uapi: Add ARM SCMI definitions Cristian Marussi
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Allow protocols themselves to register for their own notifications and
providing their own notifier callbacks. While at that, allow for a protocol
to register events with compilation-time unknown report/event sizes: such
events will use the maximum transport size.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h    |  4 ++++
 drivers/firmware/arm_scmi/driver.c    | 12 ++++++++++++
 drivers/firmware/arm_scmi/notify.c    | 27 ++++++++++++++++++++-------
 drivers/firmware/arm_scmi/notify.h    |  8 ++++++--
 drivers/firmware/arm_scmi/protocols.h |  8 ++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 07b9e629276d..2392ca98bc3e 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -17,6 +17,7 @@
 #include <linux/hashtable.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/refcount.h>
 #include <linux/scmi_protocol.h>
 #include <linux/spinlock.h>
@@ -505,5 +506,8 @@ static struct platform_driver __drv = {					       \
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
 					 void *priv);
 void *scmi_notification_instance_data_get(const struct scmi_handle *handle);
+int scmi_notifier_register(const struct scmi_handle *handle, u8 proto_id,
+			   u8 evt_id, const u32 *src_id,
+			   struct notifier_block *nb);
 int scmi_inflight_count(const struct scmi_handle *handle);
 #endif /* _SCMI_COMMON_H */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 71ee25b78624..8f969d8b86a6 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1674,6 +1674,17 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
 	return pi->priv;
 }
 
+static int
+scmi_register_instance_notifier(const struct scmi_protocol_handle *ph,
+				u8 evt_id, const u32 *src_id,
+				struct notifier_block *nb)
+{
+	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+	return scmi_notifier_register(pi->handle, pi->proto->id,
+				      evt_id, src_id, nb);
+}
+
 static const struct scmi_xfer_ops xfer_ops = {
 	.version_get = version_get,
 	.xfer_get_init = xfer_get_init,
@@ -2174,6 +2185,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
 	pi->ph.hops = &helpers_ops;
 	pi->ph.set_priv = scmi_set_protocol_priv;
 	pi->ph.get_priv = scmi_get_protocol_priv;
+	pi->ph.notifier_register = scmi_register_instance_notifier;
 	refcount_set(&pi->users, 1);
 	/* proto->init is assured NON NULL by scmi_protocol_register */
 	ret = pi->proto->instance_init(&pi->ph);
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 78e9e27dc9ec..3e623c14745d 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -593,7 +593,12 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
 	if (!r_evt)
 		return -EINVAL;
 
-	if (len > r_evt->evt->max_payld_sz) {
+	/* Events with a zero max_payld_sz are sized to be of the maximum
+	 * size allowed by the transport: no need to be size-checked here
+	 * since the transport layer would have already dropped such
+	 * over-sized messages.
+	 */
+	if (r_evt->evt->max_payld_sz && len > r_evt->evt->max_payld_sz) {
 		dev_err(handle->dev, "discard badly sized message\n");
 		return -EINVAL;
 	}
@@ -752,7 +757,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 				  const struct scmi_protocol_handle *ph,
 				  const struct scmi_protocol_events *ee)
 {
-	int i;
+	int i, max_msg_sz;
 	unsigned int num_sources;
 	size_t payld_sz = 0;
 	struct scmi_registered_events_desc *pd;
@@ -767,6 +772,8 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	if (!ni)
 		return -ENOMEM;
 
+	max_msg_sz = ph->hops->get_max_msg_size(ph);
+
 	/* num_sources cannot be <= 0 */
 	if (ee->num_sources) {
 		num_sources = ee->num_sources;
@@ -779,8 +786,13 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 	}
 
 	evt = ee->evts;
-	for (i = 0; i < ee->num_events; i++)
+	for (i = 0; i < ee->num_events; i++) {
+		if (evt[i].max_payld_sz == 0) {
+			payld_sz = max_msg_sz;
+			break;
+		}
 		payld_sz = max_t(size_t, payld_sz, evt[i].max_payld_sz);
+	}
 	payld_sz += sizeof(struct scmi_event_header);
 
 	pd = scmi_allocate_registered_events_desc(ni, proto_id, ee->queue_sz,
@@ -809,7 +821,8 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 		mutex_init(&r_evt->sources_mtx);
 
 		r_evt->report = devm_kzalloc(ni->handle->dev,
-					     evt->max_report_sz, GFP_KERNEL);
+					     evt->max_report_sz ?: max_msg_sz,
+					     GFP_KERNEL);
 		if (!r_evt->report)
 			return -ENOMEM;
 
@@ -1373,9 +1386,9 @@ static int scmi_event_handler_enable_events(struct scmi_event_handler *hndl)
  *
  * Return: 0 on Success
  */
-static int scmi_notifier_register(const struct scmi_handle *handle,
-				  u8 proto_id, u8 evt_id, const u32 *src_id,
-				  struct notifier_block *nb)
+int scmi_notifier_register(const struct scmi_handle *handle,
+			   u8 proto_id, u8 evt_id, const u32 *src_id,
+			   struct notifier_block *nb)
 {
 	int ret = 0;
 	u32 evt_key;
diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h
index 76758a736cf4..ecfa4b746487 100644
--- a/drivers/firmware/arm_scmi/notify.h
+++ b/drivers/firmware/arm_scmi/notify.h
@@ -18,8 +18,12 @@
 /**
  * struct scmi_event  - Describes an event to be supported
  * @id: Event ID
- * @max_payld_sz: Max possible size for the payload of a notification message
- * @max_report_sz: Max possible size for the report of a notification message
+ * @max_payld_sz: Max possible size for the payload of a notification message.
+ *		  Set to zero to use the maximum payload size allowed by the
+ *		  transport.
+ * @max_report_sz: Max possible size for the report of a notification message.
+ *		  Set to zero to use the maximum payload size allowed by the
+ *		  transport.
  *
  * Each SCMI protocol, during its initialization phase, can describe the events
  * it wishes to support in a few struct scmi_event and pass them to the core
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index d62c4469d1fd..2e40a7bb5b01 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -161,8 +161,13 @@ struct scmi_proto_helpers_ops;
  * @dev: A reference to the associated SCMI instance device (handle->dev).
  * @xops: A reference to a struct holding refs to the core xfer operations that
  *	  can be used by the protocol implementation to generate SCMI messages.
+ * @hops: A reference to a struct holding refs to the common helper operations
+ *	  that can be used by the protocol implementation.
  * @set_priv: A method to set protocol private data for this instance.
  * @get_priv: A method to get protocol private data previously set.
+ * @notifier_register: A method to register interest for notifications from
+ *		       within a protocol implementation unit: notifiers can
+ *		       be registered only for the same protocol.
  *
  * This structure represents a protocol initialized against specific SCMI
  * instance and it will be used as follows:
@@ -182,6 +187,9 @@ struct scmi_protocol_handle {
 	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,
 			u32 version);
 	void *(*get_priv)(const struct scmi_protocol_handle *ph);
+	int (*notifier_register)(const struct scmi_protocol_handle *ph,
+				 u8 evt_id, const u32 *src_id,
+				 struct notifier_block *nb);
 };
 
 /**
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 04/10] uapi: Add ARM SCMI definitions
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (2 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-26 14:30   ` kernel test robot
  2025-10-17 15:14   ` Jonathan Cameron
  2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
                   ` (5 subsequent siblings)
  9 siblings, 2 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add a number of structures and ioctls definitions used by the ARM
SCMI Telemetry protocol.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 MAINTAINERS               |   1 +
 include/uapi/linux/scmi.h | 286 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 287 insertions(+)
 create mode 100644 include/uapi/linux/scmi.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f6206963efbf..5c10e096e638 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24552,6 +24552,7 @@ F:	drivers/regulator/scmi-regulator.c
 F:	drivers/reset/reset-scmi.c
 F:	include/linux/sc[mp]i_protocol.h
 F:	include/trace/events/scmi.h
+F:	include/uapi/linux/scmi.h
 F:	include/uapi/linux/virtio_scmi.h
 
 SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) i.MX Extension Message Protocol drivers
diff --git a/include/uapi/linux/scmi.h b/include/uapi/linux/scmi.h
new file mode 100644
index 000000000000..b1a6d34fee4a
--- /dev/null
+++ b/include/uapi/linux/scmi.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2025 ARM Ltd.
+ */
+#ifndef _UAPI_LINUX_SCMI_H
+#define _UAPI_LINUX_SCMI_H
+
+/*
+ * Userspace interface SCMI Telemetry
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define SCMI_TLM_DE_IMPL_MAX_DWORDS	4
+
+#define SCMI_TLM_GRP_INVALID            0xFFFFFFFF
+
+/**
+ * scmi_tlm_base_info - Basic info about an instance
+ *
+ * @version: SCMI Telemetry protocol version
+ * @de_impl_version: SCMI Telemetry DE implementation revision
+ * @num_de: Number of defined DEs
+ * @num_groups Number of defined DEs groups
+ * @num_intervals: Number of update intervals available (instance-level)
+ * @flags: Instance specific feature-support bitmap
+ *
+ * Used by:
+ *	RO - SCMI_TLM_GET_INFO
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_base_info {
+	__u32 version;
+	__u32 de_impl_version[SCMI_TLM_DE_IMPL_MAX_DWORDS];
+	__u32 num_des;
+	__u32 num_groups;
+	__u32 num_intervals;
+	__u32 flags;
+#define SCMI_TLM_CAN_RESET	(1 << 0)
+///TODO more flags
+};
+
+/**
+ * scmi_tlm_config  - Whole instance or group configuration
+ *
+ * @enable: Enable/Disable Telemetry for the whole instance or the group
+ * @t_enable: Enable/Disable timestamping for all the DEs belonging to a group.
+ * @current_update_interval: Get/Set currently active update interval for the
+ *			     whole instance or a group.
+ *
+ * Used by:
+ *	RO - SCMI_TLM_GET_CFG
+ *	WO - SCMI_TLM_SET_CFG
+ *
+ * Supported by:
+ *	control/
+ *	groups/<N>/control
+ */
+struct scmi_tlm_config {
+	__u8 enable;
+	__u8 t_enable;
+	__u8 reserved[2];
+	__u32 current_update_interval;
+};
+
+/**
+ * scmi_tlm_intervals  - Update intervals descriptor
+ *
+ * @discrete: Flag to indicate the nature of the intervals described in
+ *	      @update_intervals.
+ *	      When 'false' @update_intervals is a triplet: min/max/step
+ * @num: Number of entries of @available
+ * @update_intervals: A variably-sized array containing the update intervals
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_INTRVS
+ *
+ * Supported by:
+ *	control/
+ *	groups/<N>/control
+ */
+struct scmi_tlm_intervals {
+	__u8 discrete;
+	__u8 reserved[3];
+	__u32 num;
+#define SCMI_TLM_UPDATE_INTVL_SEGMENT_LOW	0
+#define SCMI_TLM_UPDATE_INTVL_SEGMENT_HIGH	1
+#define SCMI_TLM_UPDATE_INTVL_SEGMENT_STEP	2
+	__u32 update_intervals[];
+};
+
+/**
+ * scmi_tlm_de_config  - DE configuration
+ *
+ * @id: Identifier of the DE to act upon (ignored by SCMI_TLM_SET_ALL_CFG)
+ * @enable: A boolean to enable/disable the DE
+ * @t_enable: A boolean to enable/disable the timestamp for this DE
+ *	      (if supported)
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_DE_CFG
+ *	RW - SCMI_TLM_SET_DE_CFG
+ *	WO - SCMI_TLM_SET_ALL_CFG
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_de_config {
+	__u32 id;
+	__u32 enable;
+	__u32 t_enable;
+};
+
+/**
+ * scmi_tlm_de_info  - DE Descriptor
+ *
+ * @id: DE identifier
+ * @grp_id: Identifier of the group which this DE belongs to; reported as
+ *	    SCMI_TLM_GRP_INVALID when not part of any group
+ * @data_sz: DE data size in bytes
+ * @type: DE type
+ * @unit: DE unit of measurements
+ * @unit_exp: Power-of-10 multiplier for DE unit
+ * @tstamp_exp: Power-of-10 multiplier for DE timestamp (if supported)
+ * @instance_id: DE instance ID
+ * @compo_instance_id: DE component instance ID
+ * @compo_type: Type of component which is associated to this DE
+ * @peristent: Data value for this DE survives reboot (non-cold ones)
+ * @name: Optional name of this DE
+ *
+ * Used to get the full description of a DE: it reflects DE Descriptors
+ * definitions in 3.12.4.6.
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_DE_INFO
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_de_info {
+	__u32 id;
+	__u32 grp_id;
+	__u32 data_sz;
+	__u32 type;
+	__u32 unit;
+	__s32 unit_exp;
+	__s32 tstamp_exp;
+	__u32 instance_id;
+	__u32 compo_instance_id;
+	__u32 compo_type;
+	__u32 persistent;
+	__u8 name[16];
+};
+
+/**
+ * scmi_tlm_des_list  - List of all defined DEs
+ *
+ * @num_des: Number of entries in @des
+ * @des: An array containing descriptors for all defined DEs
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_DE_LIST
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_des_list {
+	__u32 num_des;
+	struct scmi_tlm_de_info des[];
+};
+
+/**
+ * scmi_tlm_de_sample - A DE reading
+ *
+ * @id: DE identifier
+ * @tstamp: DE reading timestamp (equal 0 is NOT supported)
+ * @val: Reading of the DE data value
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_DE_VALUE
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_de_sample {
+	__u32 id;
+	__u64 tstamp;
+	__u64 val;
+};
+
+/**
+ * scmi_tlm_data_read - Bulk read of multiple DEs
+ *
+ * @num_samples: Number of entries returned in @samples
+ * @samples: An array of samples containing an entry for each DE that was
+ *	     enabled when the single sample read request was issued.
+ *
+ * Used by:
+ *	RW - SCMI_TLM_SINGLE_SAMPLE
+ *	RW - SCMI_TLM_BULK_READ
+ *
+ * Supported by:
+ *	control/
+ *	groups/<N>/control
+ */
+struct scmi_tlm_data_read {
+	__u32 num_samples;
+	struct scmi_tlm_de_sample samples[];
+};
+
+/**
+ * scmi_tlm_grp_info  - DE-group descriptor
+ *
+ * @id: Group ID number
+ * @num_des: Number of DEs part of this group
+ * @num_intervals: Number of update intervals supported. Zero if group does not
+ *		   support per-group update interval configuration.
+ *
+ * Used by:
+ *	RO - SCMI_TLM_GET_GRP_INFO
+ *
+ * Supported by:
+ *	groups/<N>control/
+ */
+struct scmi_tlm_grp_info {
+	__u32 id;
+	__u32 num_des;
+	__u32 num_intervals;
+};
+
+/**
+ * scmi_tlm_grps_list  - DE-groups List
+ *
+ * @num_grps: Number of entries returned in @grps
+ * @grps: An array containing descriptors for all defined DE Groups
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_GRP_LIST
+ *
+ * Supported by:
+ *	control/
+ */
+struct scmi_tlm_grps_list {
+	__u32 num_grps;
+	struct scmi_tlm_grp_info grps[];
+};
+
+/**
+ * scmi_tlm_grp_desc  - Group descriptor
+ *
+ * @num_des: Number of DEs part of this group
+ * @composing_des: An array containing the DE IDs that belongs to this group.
+ *
+ * Used by:
+ *	RW - SCMI_TLM_GET_GRP_DESC
+ *
+ * Supported by:
+ *	groups/<N>control/
+ */
+struct scmi_tlm_grp_desc {
+	__u32 num_des;
+	__u32 composing_des[];
+};
+
+#define SCMI 0xF1
+
+#define SCMI_TLM_GET_INFO	_IOR(SCMI,  0x00, struct scmi_tlm_base_info)
+#define SCMI_TLM_GET_CFG	_IOR(SCMI,  0x01, struct scmi_tlm_config)
+#define SCMI_TLM_SET_CFG	_IOW(SCMI,  0x02, struct scmi_tlm_config)
+#define SCMI_TLM_GET_INTRVS	_IOWR(SCMI, 0x03, struct scmi_tlm_intervals)
+#define SCMI_TLM_GET_DE_CFG	_IOWR(SCMI, 0x04, struct scmi_tlm_de_config)
+#define SCMI_TLM_SET_DE_CFG	_IOWR(SCMI, 0x05, struct scmi_tlm_de_config)
+#define SCMI_TLM_GET_DE_INFO	_IOWR(SCMI, 0x06, struct scmi_tlm_de_info)
+#define SCMI_TLM_GET_DE_LIST	_IOWR(SCMI, 0x07, struct scmi_tlm_des_list)
+#define SCMI_TLM_GET_DE_VALUE	_IOWR(SCMI, 0x08, struct scmi_tlm_de_sample)
+#define SCMI_TLM_SET_ALL_CFG	_IOW(SCMI,  0x09, struct scmi_tlm_de_config)
+#define SCMI_TLM_GET_GRP_LIST	_IOWR(SCMI, 0x0A, struct scmi_tlm_grps_list)
+#define SCMI_TLM_GET_GRP_INFO	_IOR(SCMI,  0x0B, struct scmi_tlm_grp_info)
+#define SCMI_TLM_GET_GRP_DESC	_IOWR(SCMI, 0x0C, struct scmi_tlm_grp_desc)
+#define SCMI_TLM_SINGLE_SAMPLE	_IOWR(SCMI, 0x0D, struct scmi_tlm_data_read)
+#define SCMI_TLM_BULK_READ	_IOWR(SCMI, 0x0E, struct scmi_tlm_data_read)
+
+#endif /* _UAPI_LINUX_SCMI_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (3 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 04/10] uapi: Add ARM SCMI definitions Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-26 17:27   ` kernel test robot
                     ` (2 more replies)
  2025-09-25 20:35 ` [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver Cristian Marussi
                   ` (4 subsequent siblings)
  9 siblings, 3 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add basic support for SCMI V4.0-alpha_0 Telemetry protocol including SHMTI,
FastChannels, Notifications and Single Sample Reads collection methods.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/Makefile    |    2 +-
 drivers/firmware/arm_scmi/driver.c    |    2 +
 drivers/firmware/arm_scmi/protocols.h |    1 +
 drivers/firmware/arm_scmi/telemetry.c | 2117 +++++++++++++++++++++++++
 include/linux/scmi_protocol.h         |  185 ++-
 5 files changed, 2305 insertions(+), 2 deletions(-)
 create mode 100644 drivers/firmware/arm_scmi/telemetry.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 780cd62b2f78..fe55b7aa0707 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -8,7 +8,7 @@ scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
 scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
 scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
 scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
-scmi-protocols-y += pinctrl.o
+scmi-protocols-y += pinctrl.o telemetry.o
 scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
 
 obj-$(CONFIG_ARM_SCMI_PROTOCOL) += transports/
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 8f969d8b86a6..801d59e6b3bc 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -3468,6 +3468,7 @@ static int __init scmi_driver_init(void)
 	scmi_system_register();
 	scmi_powercap_register();
 	scmi_pinctrl_register();
+	scmi_telemetry_register();
 
 	return platform_driver_register(&scmi_driver);
 }
@@ -3486,6 +3487,7 @@ static void __exit scmi_driver_exit(void)
 	scmi_system_unregister();
 	scmi_powercap_unregister();
 	scmi_pinctrl_unregister();
+	scmi_telemetry_unregister();
 
 	platform_driver_unregister(&scmi_driver);
 
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index 2e40a7bb5b01..edd83a02e272 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -387,5 +387,6 @@ DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
 DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
 DECLARE_SCMI_REGISTER_UNREGISTER(system);
 DECLARE_SCMI_REGISTER_UNREGISTER(powercap);
+DECLARE_SCMI_REGISTER_UNREGISTER(telemetry);
 
 #endif /* _SCMI_PROTOCOLS_H */
diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
new file mode 100644
index 000000000000..f03000c173c2
--- /dev/null
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -0,0 +1,2117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Telemetry Protocol
+ *
+ * Copyright (C) 2025 ARM Ltd.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/xarray.h>
+
+#include "protocols.h"
+#include "notify.h"
+
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
+
+#define SCMI_TLM_TDCF_MAX_RETRIES	5
+
+enum scmi_telemetry_protocol_cmd {
+	TELEMETRY_LIST_SHMTI = 0x3,
+	TELEMETRY_DE_DESCRIPTION = 0x4,
+	TELEMETRY_LIST_UPDATE_INTERVALS = 0x5,
+	TELEMETRY_DE_CONFIGURE = 0x6,
+	TELEMETRY_DE_ENABLED_LIST = 0x7, //TODO IMPLEMENT
+	TELEMETRY_CONFIG_SET = 0x8,
+	TELEMETRY_READING_COMPLETE = TELEMETRY_CONFIG_SET,
+	TELEMETRY_CONFIG_GET = 0x9, //TODO IMPLEMENT !
+	TELEMETRY_RESET = 0xA,
+};
+
+struct scmi_msg_resp_telemetry_protocol_attributes {
+	__le32 de_num;
+	__le32 groups_num;
+	__le32 de_implementation_rev_dword[SCMI_TLM_DE_IMPL_MAX_DWORDS];
+	__le32 attributes;
+#define SUPPORTS_SINGLE_READ(x)		((x) & BIT(31))
+#define SUPPORTS_CONTINUOS_UPDATE(x)	((x) & BIT(30))
+#define SUPPORTS_PER_GROUP_CONFIG(x)	((x) & BIT(18))
+#define SUPPORTS_RESET(x)		((x) & BIT(17))
+#define SUPPORTS_FC(x)			((x) & BIT(16))
+};
+
+struct scmi_telemetry_update_notify_payld {
+	__le32 agent_id;
+	__le32 status;
+	__le32 num_dwords;
+	__le32 array[];
+};
+
+struct scmi_shmti_desc {
+	__le32 id;
+	__le32 addr_low;
+	__le32 addr_high;
+	__le32 length;
+};
+
+struct scmi_msg_resp_telemetry_shmti_list {
+	__le32 num_shmti;
+	struct scmi_shmti_desc desc[];
+};
+
+struct de_desc_fc {
+	__le32 addr_low;
+	__le32 addr_high;
+	__le32 size;
+};
+
+struct scmi_de_desc {
+	__le32 id;
+	__le32 grp_id;
+	__le32 data_sz;
+	__le32 attr_1;
+#define	IS_NAME_SUPPORTED(d)	((d)->attr_1 & BIT(31))
+#define	IS_FC_SUPPORTED(d)	((d)->attr_1 & BIT(30))
+#define	GET_DE_TYPE(d)		(le32_get_bits((d)->attr_1, GENMASK(29, 22)))
+#define	IS_PERSISTENT(d)	((d)->attr_1 & BIT(21))
+#define GET_DE_UNIT_EXP(d)						\
+	({								\
+		int __signed_exp =					\
+			le32_get_bits((d)->attr_1, GENMASK(20, 13));	\
+									\
+		if (__signed_exp & BIT(7))				\
+			__signed_exp |= GENMASK(31, 8);			\
+		__signed_exp;						\
+	})
+#define	GET_DE_UNIT(d)		(le32_get_bits((d)->attr_1, GENMASK(12, 5)))
+
+#define GET_DE_TSTAMP_EXP(d)						\
+	({								\
+		int __signed_exp =					\
+			FIELD_GET(GENMASK(4, 1), (d)->attr_1);		\
+									\
+		if (__signed_exp & BIT(3))				\
+			__signed_exp |= GENMASK(31, 4);			\
+		__signed_exp;						\
+	})
+#define	IS_TSTAMP_SUPPORTED(d)	((d)->attr_1 & BIT(0))
+	__le32 attr_2;
+#define	GET_DE_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(31, 24)))
+#define	GET_COMPO_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(23, 8)))
+#define	GET_COMPO_TYPE(d)	(le32_get_bits((d)->attr_2, GENMASK(7, 0)))
+	__le32 reserved;
+};
+
+struct scmi_msg_resp_telemetry_de_description {
+	__le32 num_desc;
+	struct scmi_de_desc desc[];
+};
+
+struct scmi_msg_telemetry_update_intervals {
+	__le32 index;
+	__le32 group_identifier;
+#define	ALL_DES_NO_GROUP	0x0
+#define SPECIFIC_GROUP_DES	0x1
+#define ALL_DES_ANY_GROUP	0x2
+	__le32 flags;
+};
+
+struct scmi_msg_resp_telemetry_update_intervals {
+	__le32 flags;
+#define INTERVALS_DISCRETE(x)	(!((x) & BIT(12)))
+	__le32 intervals[];
+};
+
+struct scmi_msg_telemetry_de_configure {
+	__le32 id;
+	__le32 flags;
+#define DE_ENABLE_NO_TSTAMP	BIT(0)
+#define DE_ENABLE_WTH_TSTAMP	BIT(1)
+#define DE_DISABLE_ALL		BIT(2)
+#define GROUP_SELECTOR		BIT(3)
+#define EVENT_DE		0
+#define EVENT_GROUP		1
+#define DE_DISABLE_ONE		0x0
+};
+
+struct scmi_msg_resp_telemetry_de_configure {
+	__le32 shmti_id;
+#define IS_SHMTI_ID_VALID(x)	((x) != 0xFFFFFFFF)
+	__le32 tdcf_de_offset;
+};
+
+struct scmi_msg_telemetry_config_set {
+	__le32 grp_id;
+	__le32 control;
+#define TELEMETRY_ENABLE		(BIT(0))
+
+#define TELEMETRY_MODE(x)		(FIELD_PREP(GENMASK(4, 1), (x)))
+#define TELEMETRY_MODE_ONDEMAND		TELEMETRY_MODE(0)
+#define TELEMETRY_MODE_NOTIFS		TELEMETRY_MODE(1)
+#define TELEMETRY_MODE_SINGLE		TELEMETRY_MODE(2)
+
+#define TELEMETRY_SELECTOR(x)		(FIELD_PREP(GENMASK(8, 5), (x)))
+#define	TELEMETRY_SELECTOR_ORPHANS	TELEMETRY_SELECTOR(0)
+#define	TELEMETRY_SELECTOR_GROUP	TELEMETRY_SELECTOR(1)
+#define	TELEMETRY_SELECTOR_ALL		TELEMETRY_SELECTOR(2)
+	__le32 sampling_rate;
+};
+
+struct scmi_msg_resp_telemetry_reading_complete {
+	__le32 num_dwords;
+	__le32 dwords[];
+};
+
+/* TDCF */
+
+#define TO_CPU_64(h, l)	(((u64)le32_to_cpu((h)) << 32) | le32_to_cpu((l)))
+
+struct fc_line {
+	u32 data_low;
+	u32 data_high;
+};
+
+struct fc_tsline {
+	u32 data_low;
+	u32 data_high;
+	u32 ts_low;
+	u32 ts_high;
+};
+
+struct line {
+	u32 data_low;
+	u32 data_high;
+};
+
+struct blk_tsline {
+	u32 ts_low;
+	u32 ts_high;
+};
+
+struct tsline {
+	u32 data_low;
+	u32 data_high;
+	u32 ts_low;
+	u32 ts_high;
+};
+
+#define LINE_DATA_GET(f)				\
+({							\
+	typeof(f) _f = (f);				\
+							\
+	(TO_CPU_64((_f)->data_high, (_f)->data_low));	\
+})
+
+#define LINE_TSTAMP_GET(f)				\
+({							\
+	typeof(f) _f = (f);				\
+							\
+	(TO_CPU_64((_f)->ts_high, (_f)->ts_low));	\
+})
+
+#define BLK_TSTAMP_GET(f)	LINE_TSTAMP_GET(f)
+
+struct payload {
+	u32 meta;
+#define IS_BLK_TS(x)	((x)->meta & BIT(4))
+#define USE_BLK_TS(x)	((x)->meta & BIT(3))
+#define USE_LINE_TS(x)	((x)->meta & BIT(2))
+#define TS_VALID(x)	((x)->meta & BIT(1))
+#define	DATA_INVALID(x) ((x)->meta & BIT(0))
+	u32 id;
+	union {
+		struct line l;
+		struct tsline tsl;
+		struct blk_tsline blk_tsl;
+	};
+};
+
+#define PAYLD_ID(x)	(le32_to_cpu(((struct payload *)(x))->id))
+
+#define LINE_DATA_PAYLD_WORDS						       \
+	((sizeof(u32) + sizeof(u32) + sizeof(struct line)) / sizeof(u32))
+#define TS_LINE_DATA_PAYLD_WORDS					       \
+	((sizeof(u32) + sizeof(u32) + sizeof(struct tsline)) / sizeof(u32))
+
+#define QWORDS_LINE_DATA_PAYLD		(LINE_DATA_PAYLD_WORDS / 2)
+#define QWORDS_TS_LINE_DATA_PAYLD	(TS_LINE_DATA_PAYLD_WORDS / 2)
+
+struct prlg {
+	u32 seq_low;
+	u32 seq_high;
+	u32 num_qwords;
+	u32 _meta_header_high;
+};
+
+struct eplg {
+	u32 seq_low;
+	u32 seq_high;
+};
+
+#define TDCF_EPLG_SZ	(sizeof(struct eplg))
+
+struct tdcf {
+	struct prlg prlg;
+	unsigned char payld[];
+};
+
+#define SHMTI_MIN_SIZE	(sizeof(struct tdcf) + TDCF_EPLG_SZ)
+
+#define TDCF_START_SEQ_GET(x)					\
+	({							\
+		u64 _val;					\
+		struct prlg *_p = &((x)->prlg);			\
+								\
+		_val = TO_CPU_64(_p->seq_high, _p->seq_low);	\
+		(_val);						\
+	})
+
+#define IS_BAD_START_SEQ(s)	((s) & 0x1)
+
+#define	TDCF_END_SEQ_GET(e)					\
+	({							\
+		u64 _val;					\
+		struct eplg *_e = (e);				\
+								\
+		_val = TO_CPU_64(_e->seq_high, _e->seq_low);	\
+		(_val);						\
+	 })
+
+struct telemetry_shmti {
+	int id;
+	void __iomem *base;
+	u32 len;
+	u64 last_magic;
+};
+
+#define SHMTI_EPLG(s)						\
+	({							\
+		struct telemetry_shmti *_s = (s);		\
+		void *_eplg;					\
+								\
+		_eplg = _s->base + _s->len - TDCF_EPLG_SZ;	\
+		(_eplg);					\
+	})
+
+struct telemetry_info {
+	bool streaming_mode;
+	int num_shmti;
+	struct device *dev;
+	struct telemetry_shmti *shmti;
+	struct xarray xa_des;
+	struct xarray xa_bts;
+	struct scmi_telemetry_info info;
+	struct notifier_block telemetry_nb;
+};
+
+#define telemetry_nb_to_info(x)	\
+	container_of(x, struct telemetry_info, telemetry_nb)
+
+struct telemetry_block_ts {
+	refcount_t users;
+	/* Protect block_ts accesses  */
+	struct mutex mtx;
+	u64 last_ts;
+	u64 last_magic;
+	struct payload __iomem *payld;
+	struct xarray *xa_bts;
+};
+
+struct telemetry_de {
+	bool cached;
+	void __iomem *base;
+	void __iomem *eplg;
+	u32 offset;
+	/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */
+	u32 fc_size;
+	/* Protect last_val/ts/magic accesses  */
+	struct mutex mtx;
+	u64 last_val;
+	u64 last_ts;
+	u64 last_magic;
+	struct telemetry_block_ts *bts;
+	struct scmi_telemetry_de de;
+};
+
+#define to_tde(d)	container_of(d, struct telemetry_de, de)
+
+struct scmi_tlm_de_priv {
+	struct telemetry_info *ti;
+	void *next;
+};
+
+static int
+scmi_telemetry_protocol_attributes_get(const struct scmi_protocol_handle *ph,
+				       struct telemetry_info *ti)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_telemetry_protocol_attributes *resp;
+
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+				      0, sizeof(*resp), &t);
+	if (ret)
+		return ret;
+
+	resp = t->rx.buf;
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		__le32 attr = resp->attributes;
+
+		ti->info.base.num_des = le32_to_cpu(resp->de_num);
+		ti->info.base.num_groups = le32_to_cpu(resp->groups_num);
+		for (int i = 0; i < SCMI_TLM_DE_IMPL_MAX_DWORDS; i++)
+			ti->info.base.de_impl_version[i] =
+				le32_to_cpu(resp->de_implementation_rev_dword[i]);
+		ti->info.single_read_support = SUPPORTS_SINGLE_READ(attr);
+		ti->info.continuos_update_support = SUPPORTS_CONTINUOS_UPDATE(attr);
+		ti->info.per_group_config_support = SUPPORTS_PER_GROUP_CONFIG(attr);
+		ti->info.reset_support = SUPPORTS_RESET(attr);
+		ti->info.fc_support = SUPPORTS_FC(attr);
+		ti->num_shmti = le32_get_bits(attr, GENMASK(15, 0));
+		/* Allocate DEs descriptors */
+		ti->info.des = devm_kcalloc(ph->dev, ti->info.base.num_des,
+					    sizeof(*ti->info.des), GFP_KERNEL);
+		if (!ti->info.des) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		/* Allocate a set of contiguous DE info descriptors. */
+		ti->info.des_store = devm_kcalloc(ph->dev, ti->info.base.num_des,
+						  sizeof(*ti->info.des_store),
+						  GFP_KERNEL);
+		if (!ti->info.des_store) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		/* Allocate DE GROUPS descriptors */
+		ti->info.groups = devm_kcalloc(ph->dev, ti->info.base.num_groups,
+					       sizeof(*ti->info.groups), GFP_KERNEL);
+		if (!ti->info.groups) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		/* Allocate a set of contiguous Group info descriptors. */
+		ti->info.grps_store = devm_kcalloc(ph->dev, ti->info.base.num_groups,
+						   sizeof(*ti->info.grps_store),
+						   GFP_KERNEL);
+		if (!ti->info.grps_store) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		for (int i = 0; i < ti->info.base.num_groups; i++) {
+			ti->info.grps_store[i].id = i;
+			/* Bind contiguous Group info struct */
+			ti->info.groups[i].info = &ti->info.grps_store[i];
+		}
+	}
+
+out:
+	ph->xops->xfer_put(ph, t);
+
+	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));
+
+	/* Initialized to first descriptor */
+	p->next = (void *)r->desc;
+
+	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 telemetry_de *tde;
+	struct scmi_tlm_de_priv *p = priv;
+	const struct scmi_de_desc *desc = p->next;
+	unsigned int grp_id;
+	int ret;
+
+	tde = to_tde(p->ti->info.des[st->desc_index + st->loop_idx]);
+
+	tde->de.info->id = le32_to_cpu(desc->id);
+	grp_id = le32_to_cpu(desc->grp_id);
+	if (grp_id != SCMI_TLM_GRP_INVALID) {
+		/* Group descriptors are empty but allocated at this point */
+		if (grp_id >= p->ti->info.base.num_groups)
+			return -EINVAL;
+
+		/* Link to parent group */
+		tde->de.info->grp_id = grp_id;
+		tde->de.grp = &p->ti->info.groups[grp_id];
+	}
+	tde->de.info->data_sz = le32_to_cpu(desc->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->tstamp_exp = GET_DE_TSTAMP_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->de.tstamp_support = IS_TSTAMP_SUPPORTED(desc);
+	tde->de.fc_support = IS_FC_SUPPORTED(desc);
+	tde->de.name_support = IS_NAME_SUPPORTED(desc);
+	p->next += sizeof(*desc);
+	if (tde->de.fc_support) {
+		u32 size;
+		u64 phys_addr;
+		void __iomem *addr;
+		struct de_desc_fc *dfc;
+
+		dfc = p->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(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 */
+		p->next += sizeof(*dfc);
+	}
+
+	if (tde->de.name_support) {
+		const char *de_name = p->next;
+
+		strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE);
+		//tde->de.name = tde->name;
+
+		/* Variably sized depending on name support */
+		p->next += SCMI_SHORT_NAME_MAX_SIZE;
+	}
+
+	/* Store DE pointer by de_id */
+	ret = xa_insert(&p->ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL);
+	if (ret)
+		return ret;
+
+	/* 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)
+{
+	/* Allocate all groups DEs IDs arrays at first ... */
+	for (int i = 0; i < ti->info.base.num_groups; i++) {
+		struct scmi_telemetry_group *grp = &ti->info.groups[i];
+
+		grp->des = devm_kcalloc(dev, grp->info->num_des,
+					sizeof(unsigned int), GFP_KERNEL);
+		if (!grp->des)
+			return -ENOMEM;
+
+		/*
+		 * Max size 32bit ID string in Hex: 0xCAFECAFE
+		 *  - 10 digits + ' '/'\n' = 11 bytes per  number
+		 *  - terminating NUL character
+		 */
+		grp->des_str_sz = grp->info->num_des * 11 + 1;
+		grp->des_str = devm_kzalloc(dev, grp->des_str_sz, GFP_KERNEL);
+		if (!grp->des_str)
+			return -ENOMEM;
+
+		/* Reset group DE counter */
+		grp->info->num_des = 0;
+	}
+
+	/* Scan DEs and populate DE IDs arrays for all groups */
+	for (int i = 0; i < ti->info.base.num_des; i++) {
+		struct scmi_telemetry_group *grp = ti->info.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 compsing DES string */
+	for (int i = 0; i < ti->info.base.num_groups; i++) {
+		struct scmi_telemetry_group *grp = &ti->info.groups[i];
+		char *buf = grp->des_str;
+		size_t bufsize = grp->des_str_sz;
+
+		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%04X%c",
+					ti->info.des[grp->des[j]]->info->id, term);
+
+			buf += len;
+			bufsize -= len;
+		}
+	}
+
+	return 0;
+}
+
+static int
+scmi_telemetry_de_descriptors_get(const struct scmi_protocol_handle *ph,
+				  struct telemetry_info *ti)
+{
+	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;
+
+	xa_init(&ti->xa_des);
+	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 scmi_telemetry_de_groups_init(ph->dev, ti);
+}
+
+static int scmi_telemetry_enumerate_de(const struct scmi_protocol_handle *ph,
+				       struct telemetry_info *ti)
+{
+	int ret;
+
+	if (!ti->info.base.num_des)
+		return 0;
+
+	for (int i = 0; i < ti->info.base.num_des; i++) {
+		struct telemetry_de *tde;
+
+		tde = devm_kzalloc(ph->dev, sizeof(*tde), GFP_KERNEL);
+		if (!tde)
+			return -ENOMEM;
+
+		mutex_init(&tde->mtx);
+
+		/* Bind contiguous DE info structures */
+		tde->de.info = &ti->info.des_store[i];
+		ti->info.des[i] = &tde->de;
+	}
+
+	ret = scmi_telemetry_de_descriptors_get(ph, ti);
+	if (ret) {
+		dev_err(ph->dev, "Cannot get DE descriptors");
+		return ret;
+	}
+
+	return 0;
+}
+
+struct scmi_tlm_ivl_priv {
+	struct device *dev;
+	struct scmi_tlm_intervals **intrvs;
+	unsigned int grp_id;
+	unsigned int flags;
+};
+
+static void iter_intervals_prepare_message(void *message,
+					   unsigned int desc_index,
+					   const void *priv)
+{
+	struct scmi_msg_telemetry_update_intervals *msg = message;
+	const struct scmi_tlm_ivl_priv *p = priv;
+
+	msg->index = cpu_to_le32(desc_index);
+	msg->group_identifier = cpu_to_le32(p->grp_id);
+	msg->flags = FIELD_PREP(GENMASK(3, 0), p->flags);
+}
+
+static int iter_intervals_update_state(struct scmi_iterator_state *st,
+				       const void *response, void *priv)
+{
+	const struct scmi_msg_resp_telemetry_update_intervals *r = response;
+
+	st->num_returned = le32_get_bits(r->flags, GENMASK(11, 0));
+	st->num_remaining = le32_get_bits(r->flags, GENMASK(31, 16));
+
+	/*
+	 * total intervals is not declared previously anywhere so we
+	 * assume it's returned+remaining on first call.
+	 */
+	if (!st->max_resources) {
+		struct scmi_tlm_ivl_priv *p = priv;
+		struct scmi_tlm_intervals *intrvs;
+		int inum;
+
+		inum = st->num_returned + st->num_remaining;
+		intrvs = devm_kzalloc(p->dev,
+				      sizeof(*intrvs) + inum * sizeof(__u32),
+				      GFP_KERNEL);
+		if (!intrvs)
+			return -ENOMEM;
+
+		intrvs->discrete = INTERVALS_DISCRETE(r->flags);
+		/* Check consistency on first call */
+		if (!intrvs->discrete &&
+		    (st->num_returned != 3 || st->num_remaining != 0))
+			return -EINVAL;
+
+		intrvs->num = inum;
+		st->max_resources = intrvs->num;
+
+		*p->intrvs = intrvs;
+	}
+
+	return 0;
+}
+
+static int
+iter_intervals_process_response(const struct scmi_protocol_handle *ph,
+				const void *response,
+				struct scmi_iterator_state *st, void *priv)
+{
+	const struct scmi_msg_resp_telemetry_update_intervals *r = response;
+	struct scmi_tlm_ivl_priv *p = priv;
+	struct scmi_tlm_intervals *intrvs = *p->intrvs;
+	unsigned int idx = st->loop_idx;
+
+	intrvs->update_intervals[st->desc_index + idx] = r->intervals[idx];
+
+	return 0;
+}
+
+static int
+scmi_tlm_enumerate_update_intervals(const struct scmi_protocol_handle *ph,
+				    struct telemetry_info *ti, int grp_id,
+				    unsigned int flags)
+{
+	struct scmi_iterator_ops ops = {
+		.prepare_message = iter_intervals_prepare_message,
+		.update_state = iter_intervals_update_state,
+		.process_response = iter_intervals_process_response,
+	};
+	struct scmi_tlm_ivl_priv ipriv = {
+		.dev = ph->dev,
+		.grp_id = grp_id,
+		.intrvs = (grp_id == SCMI_TLM_GRP_INVALID) ?
+			&ti->info.intervals :
+			&ti->info.groups[grp_id].intervals,
+		.flags = flags,
+	};
+	void *iter;
+
+	iter = ph->hops->iter_response_init(ph, &ops, 0,
+					    TELEMETRY_LIST_UPDATE_INTERVALS,
+			     sizeof(struct scmi_msg_telemetry_update_intervals),
+					    &ipriv);
+	if (IS_ERR(iter))
+		return PTR_ERR(iter);
+
+	return ph->hops->iter_response_run(iter);
+}
+
+static int
+scmi_telemetry_enumerate_update_intervals(const struct scmi_protocol_handle *ph,
+					  struct telemetry_info *ti)
+{
+	int ret;
+	unsigned int flags;
+
+	flags = !ti->info.per_group_config_support ?
+		ALL_DES_ANY_GROUP : ALL_DES_NO_GROUP;
+
+	ret = scmi_tlm_enumerate_update_intervals(ph, ti, SCMI_TLM_GRP_INVALID,
+						  flags);
+	if (ret)
+		return ret;
+
+	/* A copy for UAPI access... */
+	ti->info.base.num_intervals = ti->info.intervals->num;
+	if (ti->info.base.num_groups && ti->info.per_group_config_support) {
+		flags = SPECIFIC_GROUP_DES;
+		for (int id = 0; id < ti->info.base.num_groups; id++) {
+			ret = scmi_tlm_enumerate_update_intervals(ph, ti, id,
+								  flags);
+			if (ret)
+				break;
+
+			ti->info.grps_store[id].num_intervals =
+				ti->info.groups[id].intervals->num;
+		}
+	}
+
+	return ret;
+}
+
+static int iter_shmti_update_state(struct scmi_iterator_state *st,
+				   const void *response, void *priv)
+{
+	const struct scmi_msg_resp_telemetry_shmti_list *r = response;
+
+	st->num_returned = le32_get_bits(r->num_shmti, GENMASK(15, 0));
+	st->num_remaining = le32_get_bits(r->num_shmti, GENMASK(31, 16));
+
+	return 0;
+}
+
+static int iter_shmti_process_response(const struct scmi_protocol_handle *ph,
+				       const void *response,
+				       struct scmi_iterator_state *st,
+				       void *priv)
+{
+	const struct scmi_msg_resp_telemetry_shmti_list *r = response;
+	struct telemetry_info *ti = priv;
+	struct telemetry_shmti *shmti;
+	const struct scmi_shmti_desc *desc;
+	void __iomem *addr;
+	u64 phys_addr;
+	u32 len;
+
+	desc = &r->desc[st->loop_idx];
+	shmti = &ti->shmti[st->desc_index + st->loop_idx];
+
+	shmti->id = le32_to_cpu(desc->id);
+	phys_addr = le32_to_cpu(desc->addr_low);
+	phys_addr |= (u64)le32_to_cpu(desc->addr_high) << 32;
+
+	len = le32_to_cpu(desc->length);
+	if (len < SHMTI_MIN_SIZE)
+		return -EINVAL;
+
+	addr = devm_ioremap(ph->dev, phys_addr, len);
+	if (!addr)
+		return -EADDRNOTAVAIL;
+
+	shmti->base = addr;
+	shmti->len = len;
+
+	return 0;
+}
+
+static int scmi_telemetry_shmti_list(const struct scmi_protocol_handle *ph,
+				     struct telemetry_info *ti)
+{
+	struct scmi_iterator_ops ops = {
+		.prepare_message = iter_tlm_prepare_message,
+		.update_state = iter_shmti_update_state,
+		.process_response = iter_shmti_process_response,
+	};
+	void *iter;
+
+	iter = ph->hops->iter_response_init(ph, &ops, ti->info.base.num_des,
+					    TELEMETRY_LIST_SHMTI,
+					    sizeof(u32), ti);
+	if (IS_ERR(iter))
+		return PTR_ERR(iter);
+
+	return ph->hops->iter_response_run(iter);
+}
+
+static int scmi_telemetry_enumerate_shmti(const struct scmi_protocol_handle *ph,
+					  struct telemetry_info *ti)
+{
+	int ret;
+
+	if (!ti->num_shmti)
+		return 0;
+
+	ti->shmti = devm_kcalloc(ph->dev, ti->num_shmti, sizeof(*ti->shmti),
+				 GFP_KERNEL);
+	if (!ti->shmti)
+		return -ENOMEM;
+
+	ret = scmi_telemetry_shmti_list(ph, ti);
+	if (ret) {
+		dev_err(ph->dev, "Cannot get SHMTI list descriptors");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct scmi_telemetry_info *
+scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	return &ti->info;
+}
+
+static const struct scmi_tlm_de_info *
+scmi_telemetry_de_info_get(const struct scmi_protocol_handle *ph, u32 id)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	return xa_load(&ti->xa_des, id);
+}
+
+static u64
+scmi_telemetry_blkts_read(u64 magic, struct telemetry_block_ts *bts)
+{
+	if (WARN_ON(!bts || !refcount_read(&bts->users)))
+		return 0;
+
+	guard(mutex)(&bts->mtx);
+
+	if (bts->last_magic == magic)
+		return bts->last_ts;
+
+	bts->last_ts = BLK_TSTAMP_GET(&bts->payld->blk_tsl);
+	bts->last_magic = magic;
+
+	return bts->last_ts;
+}
+
+static void scmi_telemetry_blkts_update(u64 magic,
+					struct telemetry_block_ts *bts)
+{
+	guard(mutex)(&bts->mtx);
+
+	if (bts->last_magic != magic) {
+		bts->last_ts = BLK_TSTAMP_GET(&bts->payld->blk_tsl);
+		bts->last_magic = magic;
+	}
+}
+
+static void scmi_telemetry_blkts_put(struct device *dev,
+				     struct telemetry_block_ts *bts)
+{
+	if (refcount_dec_and_test(&bts->users)) {
+		scoped_guard(mutex, &bts->mtx)
+			xa_erase(bts->xa_bts, (unsigned long)bts->payld);
+		devm_kfree(dev, bts);
+	}
+}
+
+static struct telemetry_block_ts *
+scmi_telemetry_blkts_get(struct xarray *xa_bts, struct payload *payld)
+{
+	struct telemetry_block_ts *bts;
+
+	bts = xa_load(xa_bts, (unsigned long)payld);
+	if (!bts)
+		return NULL;
+
+	refcount_inc(&bts->users);
+
+	return bts;
+}
+
+static struct payload *
+scmi_telemetry_nearest_blk_ts(struct telemetry_shmti *shmti,
+			      struct payload *last_payld)
+{
+	struct payload *payld, *bts_payld = NULL;
+	struct tdcf __iomem *tdcf = shmti->base;
+	u32 *next;
+
+	/* Scan from start of TDCF payloads up to last_payld */
+	payld = (struct payload *)tdcf->payld;
+	next = (u32 *)payld;
+	while (payld < last_payld) {
+		if (IS_BLK_TS(payld))
+			bts_payld = payld;
+
+		next += USE_LINE_TS(payld) ?
+			TS_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS;
+		payld = (struct payload *)next;
+	}
+
+	return bts_payld;
+}
+
+static struct telemetry_block_ts *
+scmi_telemetry_blkts_lookup(struct device *dev, struct xarray *xa_bts,
+			    struct payload *payld)
+{
+	struct telemetry_block_ts *bts;
+
+	bts = xa_load(xa_bts, (unsigned long)payld);
+	if (!bts) {
+		int ret;
+
+		bts = devm_kzalloc(dev, sizeof(*bts), GFP_KERNEL);
+		if (!bts)
+			return NULL;
+
+		refcount_set(&bts->users, 1);
+		bts->payld = payld;
+		bts->xa_bts = xa_bts;
+		mutex_init(&bts->mtx);
+		ret = xa_insert(xa_bts, (unsigned long)payld, bts, GFP_KERNEL);
+		if (ret) {
+			devm_kfree(dev, bts);
+			return NULL;
+		}
+	}
+
+	return bts;
+}
+
+static struct telemetry_block_ts *
+scmi_telemetry_blkts_bind(struct device *dev, struct telemetry_shmti *shmti,
+			  struct payload *payld, struct xarray *xa_bts)
+{
+	struct telemetry_block_ts *bts;
+	struct payload *bts_payld;
+
+	/* Find the BLK_TS immediately preceding this DE payld */
+	bts_payld = scmi_telemetry_nearest_blk_ts(shmti, payld);
+	if (!bts_payld)
+		return NULL;
+
+	bts = scmi_telemetry_blkts_get(xa_bts, bts_payld);
+	if (bts)
+		return bts;
+
+	return scmi_telemetry_blkts_lookup(dev, xa_bts, payld);
+}
+
+static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
+					    struct payload __iomem *payld,
+					    struct telemetry_shmti *shmti)
+{
+	struct telemetry_block_ts *bts;
+
+	/* Check for spec compliance */
+	if (USE_LINE_TS(payld) || USE_BLK_TS(payld) ||
+	    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0))
+		return;
+
+	/* A BLK_TS descriptor MUST be returned: it is found or it is crated */
+	bts = scmi_telemetry_blkts_lookup(ti->dev, &ti->xa_bts, payld);
+	if (WARN_ON(!bts))
+		return;
+
+	/* Update the descriptor with the lastest TS*/
+	scmi_telemetry_blkts_update(shmti->last_magic, bts);
+}
+
+static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
+					   struct payload __iomem *payld,
+					   struct telemetry_shmti *shmti,
+					   bool update)
+{
+	bool ts_valid = TS_VALID(payld);
+	struct scmi_telemetry_de *de;
+	struct telemetry_de *tde;
+	u64 val, tstamp = 0;
+	u32 id;
+
+	id = PAYLD_ID(payld);
+	de = xa_load(&ti->xa_des, id);
+	if (!de)
+		return;
+
+	tde = to_tde(de);
+	/* Update DE location refs if requested: normally done only on enable */
+	if (update) {
+		tde->base = shmti->base;
+		tde->eplg = SHMTI_EPLG(shmti);
+		tde->offset = (void *)payld - (void *)shmti->base;
+	}
+
+	scoped_guard(mutex, &tde->mtx) {
+		if (tde->last_magic == shmti->last_magic)
+			return;
+	}
+
+	/* Data is always valid since we are NOT handling BLK TS lines here */
+	val = LINE_DATA_GET(&payld->l);
+	/* Collect the right TS */
+	if (ts_valid) {
+		if (USE_LINE_TS(payld)) {
+			tstamp = LINE_TSTAMP_GET(&payld->tsl);
+		} else if (USE_BLK_TS(payld)) {
+			if (!tde->bts) {
+				/*
+				 * Scanning a TDCF looking for the nearest
+				 * previous valid BLK_TS, after having found a
+				 * USE_BLK_TS() payload, MUST succeed.
+				 */
+				tde->bts = scmi_telemetry_blkts_bind(ti->dev,
+								     shmti, payld,
+								     &ti->xa_bts);
+				if (WARN_ON(!tde->bts))
+					return;
+			}
+
+			tstamp = scmi_telemetry_blkts_read(tde->last_magic,
+							   tde->bts);
+		}
+	}
+
+	guard(mutex)(&tde->mtx);
+	tde->last_magic = shmti->last_magic;
+	tde->last_val = val;
+	if (de->tstamp_enabled)
+		tde->last_ts = tstamp;
+	else
+		tde->last_ts = 0;
+}
+
+static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
+					  struct payload __iomem *payld,
+					  struct telemetry_shmti *shmti,
+					  bool update)
+{
+	int used_qwords;
+
+	used_qwords = (USE_LINE_TS(payld) && TS_VALID(payld)) ?
+		QWORDS_TS_LINE_DATA_PAYLD : QWORDS_LINE_DATA_PAYLD;
+
+	/*Invalid lines are not an error, could simply be disabled DEs */
+	if (DATA_INVALID(payld))
+		return used_qwords;
+
+	if (!IS_BLK_TS(payld))
+		scmi_telemetry_tdcf_data_parse(ti, payld, shmti, update);
+	else
+		scmi_telemetry_tdcf_blkts_parse(ti, payld, shmti);
+
+	return used_qwords;
+}
+
+static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
+				     unsigned int shmti_id, u64 ts,
+				     bool update)
+{
+	struct telemetry_shmti *shmti = &ti->shmti[shmti_id];
+	struct tdcf __iomem *tdcf = shmti->base;
+	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
+	u64 startm = 0, endm = 0xffffffffffffffff;
+	void *eplg = SHMTI_EPLG(shmti);
+
+	if (!tdcf)
+		return -ENODEV;
+
+	do {
+		unsigned int qwords;
+		void __iomem *next;
+
+		/* A bit of exponential backoff between retries */
+		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);
+
+		startm = TDCF_START_SEQ_GET(tdcf);
+		if (IS_BAD_START_SEQ(startm))
+			continue;
+
+		/* On a BAD_SEQ this will be updated on the next attempt */
+		shmti->last_magic = startm;
+
+		qwords = tdcf->prlg.num_qwords;
+		next = tdcf->payld;
+		while (qwords) {
+			int used_qwords;
+
+			used_qwords = scmi_telemetry_tdcf_line_parse(ti, next,
+								     shmti, update);
+			if (qwords < used_qwords)
+				return -EINVAL;
+
+			next += used_qwords * 8;
+			qwords -= used_qwords;
+		}
+
+		endm = TDCF_END_SEQ_GET(eplg);
+	} while (startm != endm && --retries);
+
+	if (startm != endm)
+		return -EPROTO;
+
+	return 0;
+}
+
+static int scmi_telemetry_group_state_update(struct telemetry_info *ti,
+					     struct scmi_telemetry_group *grp,
+					     bool *enable, bool *tstamp)
+{
+	struct scmi_telemetry_de *de;
+
+	for (int i = 0; i < grp->info->num_des; i++) {
+		de = ti->info.des[grp->des[i]];
+
+		if (enable)
+			de->enabled = *enable;
+		if (tstamp)
+			de->tstamp_enabled = *tstamp;
+	}
+
+	return 0;
+}
+
+static int
+scmi_telemetry_state_set_resp_process(struct telemetry_info *ti,
+				      struct scmi_telemetry_de *de,
+				      void *r, bool is_group)
+{
+	struct scmi_msg_resp_telemetry_de_configure *resp = r;
+	u32 sid = le32_to_cpu(resp->shmti_id);
+
+	/* Update DE SHMTI and offset, if applicable */
+	if (IS_SHMTI_ID_VALID(sid)) {
+		if (sid >= ti->num_shmti)
+			return -EPROTO;
+
+		/*
+		 * Update SHMTI/offset while skipping non-SHMTI-DEs like
+		 * FCs and notif-only.
+		 */
+		if (!is_group) {
+			struct telemetry_de *tde;
+			struct payload *payld;
+			u32 offs;
+
+			offs = le32_to_cpu(resp->tdcf_de_offset);
+			if (offs >= ti->shmti[sid].len - de->info->data_sz)
+				return -EPROTO;
+
+			tde = to_tde(de);
+			tde->base = ti->shmti[sid].base;
+			tde->offset = offs;
+			/* A handy reference to the Epilogue updated */
+			tde->eplg = SHMTI_EPLG(&ti->shmti[sid]);
+
+			payld = tde->base + tde->offset;
+			if (USE_BLK_TS(payld) && !tde->bts) {
+				tde->bts = scmi_telemetry_blkts_bind(ti->dev,
+								     &ti->shmti[sid],
+								     payld,
+								     &ti->xa_bts);
+				if (WARN_ON(!tde->bts))
+					return -EPROTO;
+			}
+		} else {
+			/*
+			 * A full SHMTI scan is needed when enabling a
+			 * group or its timestamps in order to retrieve
+			 * offsets: node that when group-timestamp is
+			 * enabled for composing DEs a re-scan is needed
+			 * since some DEs could have been relocated due
+			 * to lack of space in the TDCF.
+			 */
+			scmi_telemetry_shmti_scan(ti, sid, 0, true);
+		}
+	} else if (!is_group) {
+		struct telemetry_de *tde;
+
+		tde = to_tde(de);
+		if (tde->bts) {
+			/* Unlink the related BLK_TS on disable */
+			scmi_telemetry_blkts_put(ti->dev, tde->bts);
+			tde->bts = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int __scmi_telemetry_state_set(const struct scmi_protocol_handle *ph,
+				      bool is_group, bool *enable,
+				      bool *enabled_state, bool *tstamp,
+				      bool *tstamp_enabled_state, void *obj)
+{
+	struct scmi_msg_resp_telemetry_de_configure *resp;
+	struct scmi_msg_telemetry_de_configure *msg;
+	struct telemetry_info *ti = ph->get_priv(ph);
+	struct scmi_telemetry_group *grp;
+	struct scmi_telemetry_de *de;
+	unsigned int obj_id;
+	struct scmi_xfer *t;
+	int ret;
+
+	if (!enabled_state || !tstamp_enabled_state)
+		return -EINVAL;
+
+	/* Is anything to do at all on this DE ? */
+	if (!is_group && (!enable || *enable == *enabled_state) &&
+	    (!tstamp || *tstamp == *tstamp_enabled_state))
+		return 0;
+
+	/*
+	 * DE is currently disabled AND no enable state change was requested,
+	 * while timestamp is being changed: update only local state...no need
+	 * to send a message.
+	 */
+	if (!is_group && !enable && !*enabled_state) {
+		*tstamp_enabled_state = *tstamp;
+		return 0;
+	}
+
+	if (!is_group) {
+		de = obj;
+		obj_id = de->info->id;
+	} else {
+		grp = obj;
+		obj_id = grp->info->id;
+	}
+
+	ret = ph->xops->xfer_get_init(ph, TELEMETRY_DE_CONFIGURE,
+				      sizeof(*msg), sizeof(*resp), &t);
+	if (ret)
+		return ret;
+
+	msg = t->tx.buf;
+	/* Note that BOTH DE and GROUPS have a first ID field.. */
+	msg->id = cpu_to_le32(obj_id);
+	/* Default to disable mode for one DE */
+	msg->flags = DE_DISABLE_ONE;
+	msg->flags |= FIELD_PREP(GENMASK(3, 3),
+				 is_group ? EVENT_GROUP : EVENT_DE);
+
+	if ((!enable && *enabled_state) || (enable && *enable)) {
+		/* Already enabled but tstamp_enabled state changed */
+		if (tstamp) {
+			/* Here, tstamp cannot be NULL too */
+			msg->flags |= *tstamp ? DE_ENABLE_WTH_TSTAMP :
+				DE_ENABLE_NO_TSTAMP;
+		} else {
+			msg->flags |= *tstamp_enabled_state ?
+				DE_ENABLE_WTH_TSTAMP : DE_ENABLE_NO_TSTAMP;
+		}
+	}
+
+	resp = t->rx.buf;
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		ret = scmi_telemetry_state_set_resp_process(ti, de, resp, is_group);
+		if (!ret) {
+			/* Update cached state on success */
+			if (enable)
+				*enabled_state = *enable;
+			if (tstamp)
+				*tstamp_enabled_state = *tstamp;
+
+			if (is_group)
+				scmi_telemetry_group_state_update(ti, grp, enable,
+								  tstamp);
+		}
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_telemetry_state_get(const struct scmi_protocol_handle *ph,
+				    u32 id, bool *enabled, bool *tstamp_enabled)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+	struct scmi_telemetry_de *de;
+
+	if (!enabled || !tstamp_enabled)
+		return -EINVAL;
+
+	de = xa_load(&ti->xa_des, id);
+	if (!de)
+		return -ENODEV;
+
+	*enabled = de->enabled;
+	*tstamp_enabled = de->tstamp_enabled;
+
+	return 0;
+}
+
+static int scmi_telemetry_state_set(const struct scmi_protocol_handle *ph,
+				    bool is_group, u32 id, bool *enable,
+				    bool *tstamp)
+{
+	void *obj;
+	bool *enabled_state, *tstamp_enabled_state;
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	if (!is_group) {
+		struct scmi_telemetry_de *de;
+
+		de = xa_load(&ti->xa_des, id);
+		if (!de)
+			return -ENODEV;
+
+		enabled_state = &de->enabled;
+		tstamp_enabled_state = &de->tstamp_enabled;
+		obj = de;
+	} else {
+		struct scmi_telemetry_group *grp;
+
+		if (id >= ti->info.base.num_groups)
+			return -EINVAL;
+
+		grp = &ti->info.groups[id];
+
+		enabled_state = &grp->enabled;
+		tstamp_enabled_state = &grp->tstamp_enabled;
+		obj = grp;
+	}
+
+	return __scmi_telemetry_state_set(ph, is_group, enable, enabled_state,
+					  tstamp, tstamp_enabled_state, obj);
+}
+
+static int scmi_telemetry_all_disable(const struct scmi_protocol_handle *ph,
+				      bool is_group)
+{
+	struct scmi_msg_telemetry_de_configure *msg;
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, TELEMETRY_DE_CONFIGURE,
+				      sizeof(*msg), 0, &t);
+	if (ret)
+		return ret;
+
+	msg = t->tx.buf;
+	msg->flags = DE_DISABLE_ALL;
+	if (is_group)
+		msg->flags |= GROUP_SELECTOR;
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		struct telemetry_info *ti = ph->get_priv(ph);
+
+		for (int i = 0; i < ti->info.base.num_des; i++)
+			ti->info.des[i]->enabled = false;
+
+		if (is_group) {
+			for (int i = 0; i < ti->info.base.num_groups; i++)
+				ti->info.groups[i].enabled = false;
+		}
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int
+scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph,
+				    unsigned int res_id, bool grp_ignore,
+				    bool *enable,
+				    unsigned int *update_interval_ms,
+				    enum scmi_telemetry_collection *mode)
+{
+	enum scmi_telemetry_collection *current_mode, next_mode;
+	struct telemetry_info *ti = ph->get_priv(ph);
+	struct scmi_msg_telemetry_config_set *msg;
+	unsigned int *active_update_interval;
+	struct scmi_xfer *t;
+	bool tlm_enable;
+	u32 interval;
+	int ret;
+
+	if (mode && *mode == SCMI_TLM_NOTIFICATION &&
+	    !ti->info.continuos_update_support)
+		return -EINVAL;
+
+	if (res_id != SCMI_TLM_GRP_INVALID && res_id >= ti->info.base.num_groups)
+		return -EINVAL;
+
+	if (res_id == SCMI_TLM_GRP_INVALID || grp_ignore) {
+		active_update_interval = &ti->info.active_update_interval;
+		current_mode = &ti->info.current_mode;
+	} else {
+		active_update_interval =
+			&ti->info.groups[res_id].active_update_interval;
+		current_mode = &ti->info.groups[res_id].current_mode;
+	}
+
+	if (!enable && !update_interval_ms && (!mode || *mode == *current_mode))
+		return 0;
+
+	ret = ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_SET,
+				      sizeof(*msg), 0, &t);
+	if (ret)
+		return ret;
+
+	if (!update_interval_ms)
+		interval = cpu_to_le32(*active_update_interval);
+	else
+		interval = *update_interval_ms;
+
+	tlm_enable = enable ? *enable : ti->info.enabled;
+	next_mode = mode ? *mode : *current_mode;
+
+	msg = t->tx.buf;
+	msg->grp_id = res_id;
+	msg->control = tlm_enable ? TELEMETRY_ENABLE : 0;
+	msg->control |= grp_ignore ? TELEMETRY_SELECTOR_ALL :
+		TELEMETRY_SELECTOR_GROUP;
+	msg->control |= TELEMETRY_MODE(next_mode);
+	msg->sampling_rate = interval;
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		ti->info.enabled = tlm_enable;
+		*current_mode = next_mode;
+		ti->info.notif_enabled = *current_mode == SCMI_TLM_NOTIFICATION;
+		if (update_interval_ms)
+			*active_update_interval = interval;
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_telemetry_de_data_fc_read(struct telemetry_de *tde,
+					  u64 *tstamp, u64 *val)
+{
+	struct fc_tsline __iomem *fc = tde->base + tde->offset;
+
+	*val = LINE_DATA_GET(fc);
+	if (tstamp) {
+		if (tde->de.tstamp_support)
+			*tstamp = LINE_TSTAMP_GET(fc);
+		else
+			*tstamp = 0;
+	}
+
+	return 0;
+}
+
+static void scmi_telemetry_scan_update(struct telemetry_info *ti, u64 ts)
+{
+	/* Scan all SHMTIs ... */
+	for (int id = 0; id < ti->num_shmti; id++)
+		scmi_telemetry_shmti_scan(ti, id, ts, false);
+
+	/* ... then scan all FCs ... XXX Use a list */
+	for (int i = 0; i < ti->info.base.num_des; i++) {
+		struct scmi_telemetry_de *de;
+		struct telemetry_de *tde;
+		u64 val, tstamp;
+		int ret;
+
+		de = ti->info.des[i];
+		if (!de->enabled)
+			continue;
+
+		tde = to_tde(de);
+		if (!tde->de.fc_support)
+			continue;
+
+		//TODO Report errors
+		ret = scmi_telemetry_de_data_fc_read(tde, &tstamp, &val);
+		if (ret)
+			return;
+
+		guard(mutex)(&tde->mtx);
+		tde->last_val = val;
+		if (de->tstamp_enabled)
+			tde->last_ts = tstamp;
+		else
+			tde->last_ts = 0;
+	}
+}
+
+/*
+ * TDCF and TS Line Management Notes
+ * ---------------------------------
+ *  (from a chat with ATG)
+ *
+ * TCDF Payload Metadata notable bits:
+ *  - Bit[3]: USE BLK Tstamp
+ *  - Bit[2]: USE LINE Tstamp
+ *  - Bit[1]: Tstamp VALID
+ *
+ * CASE_1:
+ * -------
+ *	+ A DE is enabled with timestamp disabled, so the TS fields are
+ *	  NOT present
+ *	  -> BIT[3:1] = 000b
+ *
+ *	  - 1/A LINE_TSTAMP
+ *	  ------------------
+ *	     + that DE is then 're-enabled' with TS: so it was ON, it remains
+ *	       ON but using DE_CONFIGURE I now enabled also TS, so the
+ *	       platform relocates it at the end of the SHMTI and return the
+ *	       new offset
+ *	       -> BIT[3:1] = 011b
+ *
+ *	  - 1/B BLK_TSTAMP
+ *	  ------------------
+ *	     + that DE is then 're-enabled' with BLK TS: so it was ON, it
+ *	       remains ON but using DE_CONFIGURE, we now also enabled the TS,
+ *	       so the platform will:
+ *	       - IF a preceding BLK_TS line exist (with same clock freq)
+ *	         it relocates the DE at the end of the SHMTI and return the
+ *	         new offset (if there is enough room, if not in another SHMTI)
+ *	       - IF a preceding BLK_TS line DOES NOT exist (same clock freq)
+ *	         it creates a new BLK_TS line at the end of the SHMTI and then
+ *	         relocates the DE after the new BLK_TS and return the
+ *	         new offset (if there is enough room, if not in another SHMTI)
+ *	       -> BIT[3:1] = 101b
+ *
+ *	+ the hole left from the relocated DE can be reused by the platform
+ *	to fit another equally sized DE. (i.e. without shuffling around any
+ *	other enabled DE, since that would cause a change of the known offset)
+ *
+ * CASE_2:
+ * -------
+ *	+ A DE is enabled with LINE timestamp enabled, so the TS_Line is there
+ *	  -> BIT[3:1] = 011b
+ *	+ that DE has its timestamp disabled: again, you can do this without
+ *	  disabling it fully but just disabling the TS, so now that TS_line
+ *	  fields are still physiclly there but NOT valid
+ *	  -> BIT[3:1] = 010b
+ *	+ the hole from the timestamp remain there unused until
+ *		- you enable again the TS so the hole is used again
+ *		  -> BIT[3:1] = 011b
+ *			OR
+ *		- you disable fully the DE and then re-enable it with the TS
+ *		  -> potentially CASE_1 the DE is relocated on enable
+ *	+ same kind of dynamic applies if the DE had a BLK_TS line
+ */
+static int scmi_telemetry_tdcf_de_parse(struct telemetry_de *tde,
+					u64 *tstamp, u64 *val)
+{
+	struct tdcf __iomem *tdcf = tde->base;
+	u64 startm, endm;
+	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
+
+	if (!tdcf)
+		return -ENODEV;
+
+	do {
+		struct payload __iomem *payld;
+
+		/* A bit of exponential backoff between retries */
+		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);
+
+		startm = TDCF_START_SEQ_GET(tdcf);
+		if (IS_BAD_START_SEQ(startm))
+			continue;
+
+		/* Has anything changed at all at the SHMTI level ? */
+		scoped_guard(mutex, &tde->mtx) {
+			if (tde->last_magic == startm) {
+				*val = tde->last_val;
+				if (tstamp)
+					*tstamp = tde->last_ts;
+				return 0;
+			}
+		}
+
+		payld = tde->base + tde->offset;
+		if (DATA_INVALID(payld))
+			return -EINVAL;
+
+		if (IS_BLK_TS(payld))
+			return -EINVAL;
+
+		if (le32_to_cpu(payld->id) != tde->de.info->id)
+			return -EINVAL;
+
+		/* Data is always valid since NOT handling BLK TS lines here */
+		*val = LINE_DATA_GET(&payld->l);
+		/* Collect the right TS */
+		if (tstamp) {
+			if (!TS_VALID(payld)) {
+				*tstamp = 0;
+			} else if (USE_LINE_TS(payld)) {
+				*tstamp = LINE_TSTAMP_GET(&payld->tsl);
+			} else if (USE_BLK_TS(payld)) {
+				/*
+				 * A valid line using BLK_TS should have been
+				 * initialized with the related BLK_TS when
+				 * enabled.
+				 */
+				if (WARN_ON(!tde->bts))
+					return -EPROTO;
+				*tstamp = scmi_telemetry_blkts_read(startm,
+								    tde->bts);
+			}
+		}
+
+		endm = TDCF_END_SEQ_GET(tde->eplg);
+	} while (startm != endm && --retries);
+
+	if (startm != endm)
+		return -EPROTO;
+
+	guard(mutex)(&tde->mtx);
+	tde->last_magic = startm;
+	tde->last_val = *val;
+	if (tstamp)
+		tde->last_ts = *tstamp;
+
+	return 0;
+}
+
+static int scmi_telemetry_de_lookup(struct telemetry_de *tde,
+				    u64 *tstamp, u64 *val)
+{
+	if (!tde->de.fc_support)
+		return scmi_telemetry_tdcf_de_parse(tde, tstamp, val);
+
+	return scmi_telemetry_de_data_fc_read(tde, tstamp, val);
+}
+
+static int scmi_telemetry_de_collect(struct telemetry_info *ti,
+				     struct scmi_telemetry_de *de,
+				     u64 *tstamp, u64 *val)
+{
+	struct telemetry_de *tde = to_tde(de);
+
+	if (!de->enabled)
+		return -EINVAL;
+
+	/*
+	 * DE readings returns cached values when:
+	 *  - DE data value was retrieved via notification
+	 */
+	scoped_guard(mutex, &tde->mtx) {
+		if (tde->cached) {
+			*val = tde->last_val;
+			if (tstamp)
+				*tstamp = tde->last_ts;
+			return 0;
+		}
+	}
+
+	return scmi_telemetry_de_lookup(tde, tstamp, val);
+}
+
+static int scmi_telemetry_de_cached_read(struct telemetry_info *ti,
+					 struct scmi_telemetry_de *de,
+					 u64 *tstamp, u64 *val)
+{
+	struct telemetry_de *tde = to_tde(de);
+
+	if (!de->enabled)
+		return -EINVAL;
+
+	guard(mutex)(&tde->mtx);
+	*val = tde->last_val;
+	if (tstamp)
+		*tstamp = tde->last_ts;
+
+	return 0;
+}
+
+static int scmi_telemetry_de_data_read(const struct scmi_protocol_handle *ph,
+				       struct scmi_telemetry_de_sample *sample)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+	struct scmi_telemetry_de *de;
+
+	if (!ti->info.enabled || !sample)
+		return -EINVAL;
+
+	de = xa_load(&ti->xa_des, sample->id);
+	if (!de)
+		return -ENODEV;
+
+	return scmi_telemetry_de_collect(ti, de, &sample->tstamp, &sample->val);
+}
+
+static int
+scmi_telemetry_samples_collect(struct telemetry_info *ti, int grp_id,
+			       int *num_samples,
+			       struct scmi_telemetry_de_sample *samples)
+{
+	int max_samples;
+
+	max_samples = *num_samples;
+	*num_samples = 0;
+
+	for (int i = 0; i < ti->info.base.num_des; i++) {
+		struct scmi_telemetry_de *de;
+		u64 val, tstamp;
+		int ret;
+
+		de = ti->info.des[i];
+		if (grp_id != SCMI_TLM_GRP_INVALID &&
+		    (!de->grp || de->grp->info->id != grp_id))
+			continue;
+
+		ret = scmi_telemetry_de_cached_read(ti, de, &tstamp, &val);
+		if (ret)
+			continue;
+
+		if (*num_samples == max_samples)
+			return -ENOSPC;
+
+		samples[*num_samples].tstamp = tstamp;
+		samples[*num_samples].val = val;
+		samples[*num_samples].id = de->info->id;
+
+		(*num_samples)++;
+	}
+
+	return 0;
+}
+
+static int scmi_telemetry_des_bulk_read(const struct scmi_protocol_handle *ph,
+					int grp_id, int *num_samples,
+					struct scmi_telemetry_de_sample *samples)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	if (!ti->info.enabled || !num_samples || !samples)
+		return -EINVAL;
+
+	/* Trigger a full SHMTIs & FCs scan */
+	scmi_telemetry_scan_update(ti, 0);
+
+	/* Collect all last cached values */
+	return scmi_telemetry_samples_collect(ti, grp_id, num_samples, samples);
+}
+
+static void
+scmi_telemetry_msg_payld_process(struct telemetry_info *ti,
+				 unsigned int num_dwords, unsigned int *dwords,
+				 ktime_t timestamp)
+{
+	u32 next = 0;
+
+	while (next < num_dwords) {
+		struct payload *payld = (struct payload *)&dwords[next];
+		struct scmi_telemetry_de *de;
+		struct telemetry_de *tde;
+		u32 de_id;
+
+		next += USE_LINE_TS(payld) ?
+			TS_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS;
+
+		if (DATA_INVALID(payld)) {
+			dev_err(ti->dev, "MSG - Received INVALID DATA line\n");
+			continue;
+		}
+
+		de_id = le32_to_cpu(payld->id);
+		de = xa_load(&ti->xa_des, de_id);
+		if (!de || !de->enabled) {
+			dev_err(ti->dev,
+				"MSG - Received INVALID DE - ID:%u  enabled:%d\n",
+				de_id, de ? (de->enabled ? 'Y' : 'N') : 'X');
+			continue;
+		}
+
+		tde = to_tde(de);
+		guard(mutex)(&tde->mtx);
+		tde->cached = true;
+		tde->last_val = LINE_DATA_GET(&payld->tsl);
+		//TODO BLK_TS in notification payloads
+		if (USE_LINE_TS(payld) && TS_VALID(payld))
+			tde->last_ts = LINE_TSTAMP_GET(&payld->tsl);
+		else
+			tde->last_ts = 0;
+	}
+}
+
+static int scmi_telemetry_des_sample_get(const struct scmi_protocol_handle *ph,
+					 int grp_id, int *num_samples,
+					 struct scmi_telemetry_de_sample *samples)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+	struct scmi_msg_telemetry_config_set *msg;
+	struct scmi_xfer *t;
+	bool grp_ignore;
+	int ret;
+
+	if (!ti->info.enabled || !num_samples || !samples)
+		return -EINVAL;
+
+	grp_ignore = grp_id == SCMI_TLM_GRP_INVALID ? true : false;
+	if (!grp_ignore && grp_id >= ti->info.base.num_groups)
+		return -EINVAL;
+
+	ret = ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_SET,
+				      sizeof(*msg), 0, &t);
+	if (ret)
+		return ret;
+
+	msg = t->tx.buf;
+	msg->grp_id = grp_id;
+	msg->control = TELEMETRY_ENABLE;
+	msg->control |= grp_ignore ? TELEMETRY_SELECTOR_ALL :
+		TELEMETRY_SELECTOR_GROUP;
+	msg->control |= TELEMETRY_MODE_SINGLE;
+	msg->sampling_rate = 0;
+
+	ret = ph->xops->do_xfer_with_response(ph, t);
+	if (!ret) {
+		struct scmi_msg_resp_telemetry_reading_complete *r = t->rx.buf;
+
+		/* Update cached DEs values from payload */
+		if (r->num_dwords)
+			scmi_telemetry_msg_payld_process(ti, r->num_dwords,
+							 r->dwords, 0);
+		/* Scan and update SMHTIs and FCs */
+		scmi_telemetry_scan_update(ti, 0);
+
+		/* Collect all last cached values */
+		ret = scmi_telemetry_samples_collect(ti, grp_id, num_samples,
+						     samples);
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_telemetry_config_get(const struct scmi_protocol_handle *ph,
+				     bool *enabled, int *mode,
+				     u32 *update_interval)
+{
+	return -EOPNOTSUPP;
+}
+
+static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = ph->xops->xfer_get_init(ph, TELEMETRY_RESET, sizeof(u32), 0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(0, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		struct telemetry_info *ti = ph->get_priv(ph);
+
+		//XXX Better would be to read back from platform
+		//	CONFIG_GET + DE_ENABLED_LIST
+		ti->info.enabled = false;
+		ti->info.notif_enabled = false;
+		ti->info.current_mode = SCMI_TLM_ONDEMAND;
+		ti->info.active_update_interval = 0;
+
+		for (int i = 0; i < ti->info.base.num_des; i++) {
+			ti->info.des[i]->enabled = false;
+			ti->info.des[i]->tstamp_enabled = false;
+		}
+
+		for (int i = 0; i < ti->info.base.num_groups; i++) {
+			ti->info.groups[i].enabled = false;
+			ti->info.groups[i].tstamp_enabled = false;
+			ti->info.groups[i].current_mode = SCMI_TLM_ONDEMAND;
+			ti->info.groups[i].active_update_interval = 0;
+		}
+	}
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static const struct scmi_telemetry_proto_ops tlm_proto_ops = {
+	.info_get = scmi_telemetry_info_get,
+	.de_info_get = scmi_telemetry_de_info_get,
+	.state_get = scmi_telemetry_state_get,
+	.state_set = scmi_telemetry_state_set,
+	.all_disable = scmi_telemetry_all_disable,
+	.collection_configure = scmi_telemetry_collection_configure,
+	.de_data_read = scmi_telemetry_de_data_read,
+	.des_bulk_read = scmi_telemetry_des_bulk_read,
+	.des_sample_get = scmi_telemetry_des_sample_get,
+	.config_get = scmi_telemetry_config_get,
+	.reset = scmi_telemetry_reset,
+};
+
+static bool
+scmi_telemetry_notify_supported(const struct scmi_protocol_handle *ph,
+				u8 evt_id, u32 src_id)
+{
+	struct telemetry_info *ti = ph->get_priv(ph);
+
+	return ti->info.continuos_update_support;
+}
+
+static int
+scmi_telemetry_set_notify_enabled(const struct scmi_protocol_handle *ph,
+				  u8 evt_id, u32 src_id, bool enable)
+{
+	return 0;
+}
+
+static void *
+scmi_telemetry_fill_custom_report(const struct scmi_protocol_handle *ph,
+				  u8 evt_id, ktime_t timestamp,
+				  const void *payld, size_t payld_sz,
+				  void *report, u32 *src_id)
+{
+	const struct scmi_telemetry_update_notify_payld *p = payld;
+	struct scmi_telemetry_update_report *r = report;
+
+	/* At least sized as an empty notification */
+	if (payld_sz < sizeof(*p))
+		return NULL;
+
+	r->timestamp = timestamp;
+	r->agent_id = le32_to_cpu(p->agent_id);
+	r->status = le32_to_cpu(p->status);
+	r->num_dwords = le32_to_cpu(p->num_dwords);
+	/*
+	 * Allocated dwords and report are sized as max_msg_size, so as
+	 * to allow for the maximum payload permitted by the configured
+	 * transport. Overflow is not possible since out-of-size messages
+	 * are dropped at the transport layer.
+	 */
+	if (r->num_dwords)
+		memcpy(r->dwords, p->array, r->num_dwords * sizeof(u32));
+
+	*src_id = 0;
+
+	return r;
+}
+
+static const struct scmi_event tlm_events[] = {
+	{
+		.id = SCMI_EVENT_TELEMETRY_UPDATE,
+		.max_payld_sz = 0,
+		.max_report_sz = 0,
+	},
+};
+
+static const struct scmi_event_ops tlm_event_ops = {
+	.is_notify_supported = scmi_telemetry_notify_supported,
+	.set_notify_enabled = scmi_telemetry_set_notify_enabled,
+	.fill_custom_report = scmi_telemetry_fill_custom_report,
+};
+
+static const struct scmi_protocol_events tlm_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &tlm_event_ops,
+	.evts = tlm_events,
+	.num_events = ARRAY_SIZE(tlm_events),
+	.num_sources = 1,
+};
+
+static int scmi_telemetry_notifier(struct notifier_block *nb,
+				   unsigned long event, void *data)
+{
+	struct scmi_telemetry_update_report *er = data;
+	struct telemetry_info *ti = telemetry_nb_to_info(nb);
+
+	if (er->status) {
+		dev_err(ti->dev, "Bad Telemetry update notification - ret: %dn",
+			er->status);
+		return NOTIFY_DONE;
+	}
+
+	/* Lookup the embedded DEs in the notification payload ... */
+	if (er->num_dwords)
+		scmi_telemetry_msg_payld_process(ti, er->num_dwords,
+						 er->dwords, er->timestamp);
+
+	/* ...scan the SHMTI/FCs for any other DE updates. */
+	if (ti->streaming_mode)
+		scmi_telemetry_scan_update(ti, er->timestamp);
+
+	return NOTIFY_OK;
+}
+
+static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)
+{
+	struct telemetry_info *ti;
+	u32 version;
+	int ret;
+
+	ret = ph->xops->version_get(ph, &version);
+	if (ret)
+		return ret;
+
+	dev_dbg(ph->dev, "Telemetry Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	ti = devm_kzalloc(ph->dev, sizeof(*ti), GFP_KERNEL);
+	if (!ti)
+		return -ENOMEM;
+
+	ti->dev = ph->dev;
+	xa_init(&ti->xa_bts);
+
+	ret = scmi_telemetry_protocol_attributes_get(ph, ti);
+	if (ret)
+		return ret;
+
+	ret = scmi_telemetry_enumerate_de(ph, ti);
+	if (ret)
+		return ret;
+
+	ret = scmi_telemetry_enumerate_update_intervals(ph, ti);
+	if (ret)
+		return ret;
+
+	ret = scmi_telemetry_enumerate_shmti(ph, ti);
+	if (ret)
+		return ret;
+
+	ti->info.base.version = version;
+
+	ret = ph->set_priv(ph, ti, version);
+	if (ret)
+		return ret;
+
+	/*
+	 * Register a notifier anyway straight upon protocol initialization
+	 * since there could be some DEs that are ONLY reported by notifications
+	 * even though the chosen collection method was SHMTI/FCs.
+	 */
+	if (ti->info.continuos_update_support) {
+		ti->telemetry_nb.notifier_call = &scmi_telemetry_notifier;
+		ret = ph->notifier_register(ph, SCMI_EVENT_TELEMETRY_UPDATE,
+					    NULL, &ti->telemetry_nb);
+		if (ret)
+			dev_warn(ph->dev,
+				 "Could NOT register Telemetry notifications\n");
+	}
+
+	return ret;
+}
+
+static const struct scmi_protocol scmi_telemetry = {
+	.id = SCMI_PROTOCOL_TELEMETRY,
+	.owner = THIS_MODULE,
+	.instance_init = &scmi_telemetry_protocol_init,
+	.ops = &tlm_proto_ops,
+	.events = &tlm_protocol_events,
+	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(telemetry, scmi_telemetry)
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 59527193d6dd..6c6db95d0089 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -2,7 +2,7 @@
 /*
  * SCMI Message Protocol driver header
  *
- * Copyright (C) 2018-2021 ARM Ltd.
+ * Copyright (C) 2018-2025 ARM Ltd.
  */
 
 #ifndef _LINUX_SCMI_PROTOCOL_H
@@ -13,6 +13,9 @@
 #include <linux/notifier.h>
 #include <linux/types.h>
 
+#include <uapi/linux/limits.h>
+#include <uapi/linux/scmi.h>
+
 #define SCMI_MAX_STR_SIZE		64
 #define SCMI_SHORT_NAME_MAX_SIZE	16
 #define SCMI_MAX_NUM_RATES		16
@@ -820,6 +823,176 @@ struct scmi_pinctrl_proto_ops {
 	int (*pin_free)(const struct scmi_protocol_handle *ph, u32 pin);
 };
 
+enum scmi_telemetry_de_type {
+	SCMI_TLM_DE_TYPE_USPECIFIED,
+	SCMI_TLM_DE_TYPE_ACCUMUL_IDLE_RESIDENCY,
+	SCMI_TLM_DE_TYPE_ACCUMUL_IDLE_COUNTS,
+	SCMI_TLM_DE_TYPE_ACCUMUL_OTHERS,
+	SCMI_TLM_DE_TYPE_INSTA_IDLE_STATE,
+	SCMI_TLM_DE_TYPE_INSTA_OTHERS,
+	SCMI_TLM_DE_TYPE_AVERAGE,
+	SCMI_TLM_DE_TYPE_STATUS,
+	SCMI_TLM_DE_TYPE_RESERVED_START,
+	SCMI_TLM_DE_TYPE_RESERVED_END = 0xef,
+	SCMI_TLM_DE_TYPE_OEM_START = 0xf0,
+	SCMI_TLM_DE_TYPE_OEM_END = 0xff,
+};
+
+enum scmi_telemetry_compo_type {
+	SCMI_TLM_COMPO_TYPE_USPECIFIED,
+	SCMI_TLM_COMPO_TYPE_CPU,
+	SCMI_TLM_COMPO_TYPE_CLUSTER,
+	SCMI_TLM_COMPO_TYPE_GPU,
+	SCMI_TLM_COMPO_TYPE_NPU,
+	SCMI_TLM_COMPO_TYPE_INTERCONNECT,
+	SCMI_TLM_COMPO_TYPE_MEM_CNTRL,
+	SCMI_TLM_COMPO_TYPE_L1_CACHE,
+	SCMI_TLM_COMPO_TYPE_L2_CACHE,
+	SCMI_TLM_COMPO_TYPE_L3_CACHE,
+	SCMI_TLM_COMPO_TYPE_LL_CACHE,
+	SCMI_TLM_COMPO_TYPE_SYS_CACHE,
+	SCMI_TLM_COMPO_TYPE_DISP_CNTRL,
+	SCMI_TLM_COMPO_TYPE_IPU,
+	SCMI_TLM_COMPO_TYPE_CHIPLET,
+	SCMI_TLM_COMPO_TYPE_PACKAGE,
+	SCMI_TLM_COMPO_TYPE_SOC,
+	SCMI_TLM_COMPO_TYPE_SYSTEM,
+	SCMI_TLM_COMPO_TYPE_SMCU,
+	SCMI_TLM_COMPO_TYPE_ACCEL,
+	SCMI_TLM_COMPO_TYPE_BATTERY,
+	SCMI_TLM_COMPO_TYPE_CHARGER,
+	SCMI_TLM_COMPO_TYPE_PMIC,
+	SCMI_TLM_COMPO_TYPE_BOARD,
+	SCMI_TLM_COMPO_TYPE_MEMORY,
+	SCMI_TLM_COMPO_TYPE_PERIPH,
+	SCMI_TLM_COMPO_TYPE_PERIPH_SUBC,
+	SCMI_TLM_COMPO_TYPE_LID,
+	SCMI_TLM_COMPO_TYPE_DISPLAY,
+	SCMI_TLM_COMPO_TYPE_RESERVED_START = 0x1d,
+	SCMI_TLM_COMPO_TYPE_RESERVED_END = 0xdf,
+	SCMI_TLM_COMPO_TYPE_OEM_START = 0xe0,
+	SCMI_TLM_COMPO_TYPE_OEM_END = 0xff,
+};
+
+#define	SCMI_TLM_GET_UPDATE_INTERVAL_SECS(x)				\
+	(le32_get_bits((x), GENMASK(20, 5)))
+#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x)				\
+	({								\
+		int __signed_exp = FIELD_GET(GENMASK(4, 0), (x));	\
+									\
+		if (__signed_exp & BIT(4))				\
+			__signed_exp |= GENMASK(31, 5);			\
+		__signed_exp;						\
+	})
+
+#define SCMI_TLM_BUILD_UPDATE_INTERVAL(s, e)				    \
+	(FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e)))
+
+enum scmi_telemetry_collection {
+	SCMI_TLM_ONDEMAND,
+	SCMI_TLM_NOTIFICATION,
+	SCMI_TLM_SINGLE_READ,
+};
+
+#define SCMI_TLM_GRP_INVALID		0xFFFFFFFF
+struct scmi_telemetry_group {
+	bool enabled;
+	bool tstamp_enabled;
+	unsigned int *des;
+	size_t des_str_sz;
+	char *des_str;
+	struct scmi_tlm_grp_info *info;
+	unsigned int active_update_interval;
+	struct scmi_tlm_intervals *intervals;
+	enum scmi_telemetry_collection current_mode;
+};
+
+struct scmi_telemetry_de {
+	bool tstamp_support;
+	bool fc_support;
+	bool name_support;
+	struct scmi_tlm_de_info *info;
+	struct scmi_telemetry_group *grp;
+	bool enabled;
+	bool tstamp_enabled;
+};
+
+struct scmi_telemetry_info {
+	bool single_read_support;
+	bool continuos_update_support;
+	bool per_group_config_support;
+	bool reset_support;
+	bool fc_support;
+	struct scmi_tlm_base_info base;
+	struct scmi_tlm_de_info *des_store;
+	struct scmi_telemetry_de **des;
+	struct scmi_tlm_grp_info *grps_store;
+	struct scmi_telemetry_group *groups;
+	unsigned int active_update_interval;
+	struct scmi_tlm_intervals *intervals;
+	bool enabled;
+	bool notif_enabled;
+	enum scmi_telemetry_collection current_mode;
+};
+
+struct scmi_telemetry_de_sample {
+	u32 id;
+	u64 tstamp;
+	u64 val;
+};
+
+/**
+ * struct scmi_telemetry_proto_ops - represents the various operations provided
+ *	by SCMI Telemetry Protocol
+ *
+ * @info_get: get the general Telemetry information.
+ * @de_info_get: get a specific DE information descriptor from the DE id.
+ * @state_get: retrieve the specific DE or GROUP state.
+ * @state_set: enable/disable the specific DE or GROUP with or without timestamps.
+ * @all_disable: disable ALL DEs or GROUPs.
+ * @collection_configure: choose a sampling rate and enable SHMTI/FC sampling
+ *			  for on demand collection via @de_data_read or async
+ *			  notificatioins for all the enabled DEs.
+ * @de_data_read: on-demand read of a single DE and related optional timestamp:
+ *		  the value will be retrieved at the proper SHMTI offset OR
+ *		  from the dedicated FC area (if supported by that DE).
+ * @des_bulk_read: on-demand read of all the currently enabled DEs, or just
+ *		   the ones belonging to a specific group when provided.
+ * @des_sample_get: on-demand read of all the currently enabled DEs, or just
+ *		    the ones belonging to a specific group when provided.
+ *		    This causes an immediate update platform-side of all the
+ *		    enabled DEs.
+ * @config_get: retrieve current telemetry configuration.
+ * @reset: reset configuration and telemetry data.
+ */
+struct scmi_telemetry_proto_ops {
+	const struct scmi_telemetry_info __must_check *(*info_get)
+		(const struct scmi_protocol_handle *ph);
+	const struct scmi_tlm_de_info __must_check *(*de_info_get)
+		(const struct scmi_protocol_handle *ph, u32 id);
+	int (*state_get)(const struct scmi_protocol_handle *ph,
+			 u32 id, bool *enabled, bool *tstamp_enabled);
+	int (*state_set)(const struct scmi_protocol_handle *ph,
+			 bool is_group, u32 id, bool *enable, bool *tstamp);
+	int (*all_disable)(const struct scmi_protocol_handle *ph, bool group);
+	int (*collection_configure)(const struct scmi_protocol_handle *ph,
+				    unsigned int res_id, bool grp_ignore,
+				    bool *enable,
+				    unsigned int *update_interval_ms,
+				    enum scmi_telemetry_collection *mode);
+	int (*de_data_read)(const struct scmi_protocol_handle *ph,
+			    struct scmi_telemetry_de_sample *sample);
+	int __must_check (*des_bulk_read)(const struct scmi_protocol_handle *ph,
+					  int grp_id, int *num_samples,
+					  struct scmi_telemetry_de_sample *samples);
+	int __must_check (*des_sample_get)(const struct scmi_protocol_handle *ph,
+					   int grp_id, int *num_samples,
+					   struct scmi_telemetry_de_sample *samples);
+	int (*config_get)(const struct scmi_protocol_handle *ph, bool *enabled,
+			  int *mode, u32 *update_interval);
+	int (*reset)(const struct scmi_protocol_handle *ph);
+};
+
 /**
  * struct scmi_notify_ops  - represents notifications' operations provided by
  * SCMI core
@@ -926,6 +1099,7 @@ enum scmi_std_protocol {
 	SCMI_PROTOCOL_VOLTAGE = 0x17,
 	SCMI_PROTOCOL_POWERCAP = 0x18,
 	SCMI_PROTOCOL_PINCTRL = 0x19,
+	SCMI_PROTOCOL_TELEMETRY = 0x1b,
 	SCMI_PROTOCOL_LAST = 0x7f,
 };
 
@@ -1027,6 +1201,7 @@ enum scmi_notification_events {
 	SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER = 0x0,
 	SCMI_EVENT_POWERCAP_CAP_CHANGED = 0x0,
 	SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED = 0x1,
+	SCMI_EVENT_TELEMETRY_UPDATE = 0x0,
 };
 
 struct scmi_power_state_changed_report {
@@ -1114,4 +1289,12 @@ struct scmi_powercap_meas_changed_report {
 	unsigned int	domain_id;
 	unsigned int	power;
 };
+
+struct scmi_telemetry_update_report {
+	ktime_t		timestamp;
+	unsigned int	agent_id;
+	int		status;
+	unsigned int	num_dwords;
+	unsigned int	dwords[];
+};
 #endif /* _LINUX_SCMI_PROTOCOL_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (4 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-10-20 16:23   ` Jonathan Cameron
  2025-09-25 20:35 ` [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support Cristian Marussi
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add a new SCMI System Telemetry driver which gathers platform Telemetry
data through the new the SCMI Telemetry protocol and expose all of the
discovered Telemetry data events on a dedicated pseudo-filesystem that
can be used to interactively configure SCMI Telemetry and access its
provided data.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/Kconfig             |   10 +
 drivers/firmware/arm_scmi/Makefile            |    1 +
 .../firmware/arm_scmi/scmi_system_telemetry.c | 1364 +++++++++++++++++
 3 files changed, 1375 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/scmi_system_telemetry.c

diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index e3fb36825978..9e51b3cd0c93 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -99,4 +99,14 @@ config ARM_SCMI_POWER_CONTROL
 	  called scmi_power_control. Note this may needed early in boot to catch
 	  early shutdown/reboot SCMI requests.
 
+config ARM_SCMI_SYSTEM_TELEMETRY
+	tristate "SCMI System Telemetry driver"
+	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+	help
+	  This enables SCMI Systemn Telemetry support that allows userspace to
+	  retrieve ARM Telemetry data made available via SCMI.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called scmi_system_telemetry.
+
 endmenu
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index fe55b7aa0707..20f8d55840a5 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
 obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
 
 obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
+obj-$(CONFIG_ARM_SCMI_SYSTEM_TELEMETRY) += scmi_system_telemetry.o
diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
new file mode 100644
index 000000000000..2fec465b0f33
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -0,0 +1,1364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCMI - System Telemetry Driver
+ *
+ * Copyright (C) 2025 ARM Ltd.
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitfield.h>
+#include <linux/dcache.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/kstrtox.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#define TLM_FS_MAGIC		0x75C01C80
+#define TLM_FS_NAME		"stlmfs"
+#define TLM_FS_MNT		"arm_telemetry"
+
+#define MAX_AVAILABLE_INTERV_CHAR_LENGTH	25
+#define MAX_BULK_LINE_CHAR_LENGTH		64
+#define MAX_PROP_PER_DE				12
+
+static DEFINE_MUTEX(scmi_tlm_mtx);
+static struct super_block *scmi_tlm_sb;
+
+static atomic_t scmi_tlm_instance_count = ATOMIC_INIT(0);
+
+struct scmi_tlm_setup;
+struct scmi_tlm_priv {
+	char *buf;
+	size_t buf_sz;
+	int buf_len;
+	int (*bulk_retrieve)(const struct scmi_tlm_setup *tsp,
+			     int res_id, int *num_samples,
+			     struct scmi_telemetry_de_sample *samples);
+};
+
+/**
+ * struct scmi_tlm_buffer  - Output Telemetry buffer descriptor
+ * @used: Current number of used bytes in @buf
+ * @buf: Actual buffer for output data
+ *
+ * This describes an output buffer which will be made available to each r/w
+ * entry file_operations.
+ */
+struct scmi_tlm_buffer {
+	size_t used;
+#define SCMI_TLM_MAX_BUF_SZ	128
+	unsigned char buf[SCMI_TLM_MAX_BUF_SZ];
+};
+
+/**
+ * struct scmi_tlm_setup  - Telemetry setup descriptor
+ * @dev: A reference to the related device
+ * @ops: A reference to the protocol ops
+ * @ph: A reference to the protocol handle to be used with the ops
+ */
+struct scmi_tlm_setup {
+	struct device *dev;
+	struct scmi_protocol_handle *ph;
+	const struct scmi_telemetry_proto_ops *ops;
+};
+
+/**
+ * struct scmi_tlm_class  - Telemetry class descriptor
+ * @name: A string to be used for filesystem dentry name.
+ * @mode: Filesystem mode mask.
+ * @flags: Optional misc flags that can slighly modify provided @f_op behaviour;
+ *	   this way the same @scmi_tlm_class can be used to describe multiple
+ *	   entries in the filesystem whose @f_op behaviour is very similar.
+ * @f_op: Optional file ops attached to this object. Used to initialized inodes.
+ * @i_op: Optional inode ops attached to this object. Used to initialize inodes.
+ *
+ * This structure describes a class of telemetry entities that will be
+ * associated with filesystem inodes having the same behaviour, i.e. the same
+ * @f_op and @i_op: this way it will be possible to statically define a set of
+ * common descriptors to describe all the possible behaviours and then link it
+ * to the effective inodes that will be created to support the set of DEs
+ * effectively discovered at run-time via SCMI.
+ */
+struct scmi_tlm_class {
+	const char *name;
+	umode_t mode;
+	const int flags;
+#define	TLM_IS_STATE	BIT(0)
+#define	TLM_IS_GROUP	BIT(1)
+#define IS_STATE(_f)	((_f) & TLM_IS_STATE)
+#define IS_GROUP(_f)	((_f) & TLM_IS_GROUP)
+	const struct file_operations *f_op;
+	const struct inode_operations *i_op;
+};
+
+#define TLM_ANON_CLASS(_n, _f, _m, _fo, _io)	\
+	{					\
+		.name = _n,			\
+		.flags = _f,			\
+		.f_op = _fo,			\
+		.i_op = _io,			\
+		.mode = _m,			\
+	}
+
+#define DEFINE_TLM_CLASS(_tag, _ns, _fl, _mo, _fop, _iop)	\
+	static const struct scmi_tlm_class _tag =		\
+		TLM_ANON_CLASS(_ns, _fl, _mo, _fop, _iop)
+
+/**
+ * struct scmi_tlm_inode  - Telemetry node descriptor
+ * @tsp: A reference to a structure holding data needed to interact with
+ *	 the SCMI instance associated to this inode.
+ * @cls: A reference to the @scmi_tlm_class describing the behaviour of this
+ *	 inode.
+ * @priv: Generic private data reference.
+ * @de: SCMI DE data reference.
+ * @grp: SCMI Group data reference.
+ * @info: SCMI instance information data reference.
+ * @parent: A reference to the parent inode if any.
+ * @dentry: A reference to the dentry associated to this inode.
+ * @vfs_inode: The embedded VFS inode that will be initialized and plugged
+ *	       into the live filesystem at mount time.
+ *
+ * This structure is used to describe each SCMI Telemetry entity discovered
+ * at probe time, store its related SCMI data, and link to the proper
+ * telemetry calss @scmi_tlm_class: all of these created descriptors are stored
+ * then in a root-to-leaves order at probe time, so that at mount time they can
+ * be used to build the needed filesystem entries in the proper order making use
+ * of the embeddded @vfs_inode.
+ */
+struct scmi_tlm_inode {
+	const struct scmi_tlm_setup *tsp;
+	const struct scmi_tlm_class *cls;
+	union {
+		const void *priv;
+		const struct scmi_telemetry_de *de;
+		const struct scmi_telemetry_group *grp;
+		const struct scmi_telemetry_info *info;
+	};
+	struct scmi_tlm_inode *parent;
+	struct dentry *dentry;
+	struct inode vfs_inode;
+};
+
+#define to_tlm_inode(t)	container_of(t, struct scmi_tlm_inode, vfs_inode)
+
+#define TLM_INODE_SETUP(_ti, _tsp, _cls, _parent, _ptr)		\
+({								\
+	typeof(_ti) _t = _ti;					\
+	struct scmi_tlm_inode *_ino;				\
+								\
+	if (_t->num_nodes >= _t->max_nodes)			\
+		return -ENOSPC;					\
+								\
+	_ino = scmi_tlm_inode_create(_tsp, _cls, _parent, _ptr);\
+	if (!_ino)						\
+		return -ENOMEM;					\
+								\
+	_t->all_nodes[_t->num_nodes++] = _ino;			\
+								\
+	_ino;							\
+})
+
+#define	MAX_INST_NAME		32
+
+#define TOP_NODES_NUM		32
+#define NODES_PER_DE_NUM	12
+#define NODES_PER_GRP_NUM	 9
+
+/**
+ * struct scmi_tlm_instance  - Telemetry instance descriptor
+ * @id: Progressive number identifying this probed instance; it will be used
+ *	to name the top node at the root of this instance.
+ * @name: Name to be used for the top root node of the instance. (tlm_<id>)
+ * @node: A node to link this in the list of all instances.
+ * @tsp: A reference to the SCMI instance data.
+ * @top_cls: A class to represent the top node behaviour.
+ * @top_inode: A reference to the inode at the top of this instance tree.
+ * @max_nodes: Maximum number of entries that can be hold in @all_nodes.
+ * @num_nodes: Number of nodes effectively initialized in @all_nodes
+ * @all_nodes: An array to keep track of all the initialized TLM nodes that
+ *	       have been created as a result of the usual probe time SCMI
+ *	       enumeration process.
+ * @info: A handy reference to this instance SCMI Telemetry info data.
+ *
+ * The most notable field in this structure is the @all_nodes array, which
+ * keeps tracks of all of the nodes that has been initialized at probe time,
+ * one for each SCMI Telemetry discovered entity disposed in a strict
+ * parent-child order: this way at mount time this array can be scanned in its
+ * natural order and each contained inode is initialized and plugged into the
+ * SCMI Telemetry filesystem tree.
+ */
+struct scmi_tlm_instance {
+	int id;
+	char name[MAX_INST_NAME];
+	struct list_head node;
+	struct scmi_tlm_setup *tsp;
+	struct scmi_tlm_class top_cls;
+	struct scmi_tlm_inode *top_inode;
+	int max_nodes;
+	int num_nodes;
+	struct scmi_tlm_inode **all_nodes;
+	const struct scmi_telemetry_info *info;
+};
+
+static int scmi_telemetry_instance_register(struct super_block *sb,
+					    struct scmi_tlm_instance *ti);
+
+static LIST_HEAD(scmi_telemetry_instances);
+
+static inline int
+__scmi_tlm_generic_open(struct inode *ino, struct file *filp,
+			int (*bulk_op)(const struct scmi_tlm_setup *tsp,
+				       int res_id, int *num_samples,
+				       struct scmi_telemetry_de_sample *samples))
+{
+	struct scmi_tlm_priv *tp;
+
+	tp = kzalloc(sizeof(*tp), GFP_KERNEL);
+	if (!tp)
+		return -ENOMEM;
+
+	tp->bulk_retrieve = bulk_op;
+
+	filp->private_data = tp;
+
+	return nonseekable_open(ino, filp);
+}
+
+static int scmi_tlm_priv_release(struct inode *ino, struct file *filp)
+{
+	struct scmi_tlm_priv *tp = filp->private_data;
+
+	kfree(tp->buf);
+	kfree(tp);
+
+	return 0;
+}
+
+static ssize_t scmi_tlm_all_des_write(struct file *filp,
+				      const char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	const struct scmi_telemetry_info *info = tlmi->priv;
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	const struct scmi_tlm_class *cls = tlmi->cls;
+	bool enable;
+	int ret;
+
+	ret = kstrtobool_from_user(buf, count, &enable);
+	if (ret)
+		return ret;
+
+	/* When !IS_STATE imply that is a tstamp_enable operation */
+	if (IS_STATE(cls->flags) && !enable) {
+		ret = tsp->ops->all_disable(tsp->ph, false);
+		if (ret)
+			return ret;
+	} else {
+		for (int i = 0; i < info->base.num_des; i++) {
+			const struct scmi_telemetry_de *de = info->des[i];
+
+			ret = tsp->ops->state_set(tsp->ph, false, de->info->id,
+						  IS_STATE(cls->flags) ? &enable : NULL,
+						  !IS_STATE(cls->flags) ? &enable : NULL);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return count;
+}
+
+static const struct file_operations all_des_fops = {
+	.open = nonseekable_open,
+	.write = scmi_tlm_all_des_write,
+};
+
+static ssize_t scmi_tlm_obj_enable_write(struct file *filp,
+					 const char __user *buf,
+					 size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	const struct scmi_tlm_class *cls = tlmi->cls;
+	bool enabled, is_group = IS_GROUP(cls->flags);
+	int ret, res_id;
+
+	ret = kstrtobool_from_user(buf, count, &enabled);
+	if (ret)
+		return ret;
+
+	res_id = !is_group ? tlmi->de->info->id : tlmi->grp->info->id;
+	ret = tsp->ops->state_set(tsp->ph, is_group, res_id,
+				  IS_STATE(cls->flags) ? &enabled : NULL,
+				  !IS_STATE(cls->flags) ? &enabled : NULL);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t scmi_tlm_obj_enable_read(struct file *filp, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	const bool *enabled_state, *tstamp_enabled_state;
+	char o_buf[2];
+	bool enabled;
+
+	if (!IS_GROUP(tlmi->cls->flags)) {
+		enabled_state = &tlmi->de->enabled;
+		tstamp_enabled_state = &tlmi->de->tstamp_enabled;
+	} else {
+		enabled_state = &tlmi->grp->enabled;
+		tstamp_enabled_state = &tlmi->grp->tstamp_enabled;
+	}
+
+	enabled = IS_STATE(tlmi->cls->flags) ? *enabled_state : *tstamp_enabled_state;
+	o_buf[0] = enabled ? 'Y' : 'N';
+	o_buf[1] = '\n';
+
+	return simple_read_from_buffer(buf, count, ppos, o_buf, 2);
+}
+
+static const struct file_operations obj_enable_fops = {
+	.open = nonseekable_open,
+	.write = scmi_tlm_obj_enable_write,
+	.read = scmi_tlm_obj_enable_read,
+};
+
+static int scmi_tlm_open(struct inode *ino, struct file *filp)
+{
+	struct scmi_tlm_buffer *data;
+
+	/* Allocate some per-open buffer */
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	filp->private_data = data;
+
+	return nonseekable_open(ino, filp);
+}
+
+static int scmi_tlm_release(struct inode *ino, struct file *filp)
+{
+	kfree(filp->private_data);
+
+	return 0;
+}
+
+static ssize_t
+scmi_tlm_update_interval_read(struct file *filp, char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	struct scmi_tlm_buffer *data = filp->private_data;
+	unsigned int active_update_interval;
+
+	if (!data)
+		return 0;
+
+	if (!IS_GROUP(tlmi->cls->flags))
+		active_update_interval = tlmi->info->active_update_interval;
+	else
+		active_update_interval = tlmi->grp->active_update_interval;
+
+	if (!data->used)
+		data->used =
+			scnprintf(data->buf, SCMI_TLM_MAX_BUF_SZ, "%u, %d\n",
+				  SCMI_TLM_GET_UPDATE_INTERVAL_SECS(active_update_interval),
+				  SCMI_TLM_GET_UPDATE_INTERVAL_EXP(active_update_interval));
+
+	return simple_read_from_buffer(buf, count, ppos, data->buf, data->used);
+}
+
+static ssize_t
+scmi_tlm_update_interval_write(struct file *filp, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	bool is_group = IS_GROUP(tlmi->cls->flags);
+	unsigned int update_interval_ms = 0, secs = 0;
+	int ret, grp_id, exp = -3;
+	char *kbuf, *p, *token;
+
+	kbuf = memdup_user_nul(buf, count);
+	if (IS_ERR(kbuf))
+		return PTR_ERR(kbuf);
+
+	p = kbuf;
+	token = strsep(&p, " ");
+	if (!token) {
+		/* At least one token must exist to be a valid input */
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = kstrtouint(token, 0, &secs);
+	if (ret)
+		goto err;
+
+	token = strsep(&p, " ");
+	if (token) {
+		ret = kstrtoint(token, 0, &exp);
+		if (ret)
+			goto err;
+	}
+
+	kfree(kbuf);
+
+	update_interval_ms = SCMI_TLM_BUILD_UPDATE_INTERVAL(secs, exp);
+
+	grp_id = !is_group ? SCMI_TLM_GRP_INVALID : tlmi->grp->info->id;
+	ret = tsp->ops->collection_configure(tsp->ph, grp_id, !is_group, NULL,
+					     &update_interval_ms, NULL);
+	if (ret)
+		return ret;
+
+	return count;
+
+err:
+	kfree(kbuf);
+	return ret;
+}
+
+static const struct file_operations current_interval_fops = {
+	.open = scmi_tlm_open,
+	.read = scmi_tlm_update_interval_read,
+	.write = scmi_tlm_update_interval_write,
+	.release = scmi_tlm_release,
+};
+
+static ssize_t scmi_tlm_de_read(struct file *filp, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	struct scmi_tlm_buffer *data = filp->private_data;
+	int ret;
+
+	if (!data)
+		return 0;
+
+	if (!data->used) {
+		struct scmi_telemetry_de_sample sample;
+
+		sample.id = tlmi->de->info->id;
+		ret = tsp->ops->de_data_read(tsp->ph, &sample);
+		if (ret)
+			return ret;
+
+		data->used = scnprintf(data->buf, SCMI_TLM_MAX_BUF_SZ,
+				       "%llu: %016llX\n", sample.tstamp,
+				       sample.val);
+	}
+
+	return simple_read_from_buffer(buf, count, ppos, data->buf, data->used);
+}
+
+static const struct file_operations de_read_fops = {
+	.open = scmi_tlm_open,
+	.read = scmi_tlm_de_read,
+	.release = scmi_tlm_release,
+};
+
+static ssize_t
+scmi_tlm_enable_read(struct file *filp, char __user *buf, size_t count,
+		     loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	char o_buf[2];
+
+	o_buf[0] = tlmi->info->enabled ? 'Y' : 'N';
+	o_buf[1] = '\n';
+
+	return simple_read_from_buffer(buf, count, ppos, o_buf, 2);
+}
+
+static ssize_t
+scmi_tlm_enable_write(struct file *filp, const char __user *buf, size_t count,
+		      loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	enum scmi_telemetry_collection mode = SCMI_TLM_ONDEMAND;
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	bool enabled;
+	int ret;
+
+	ret = kstrtobool_from_user(buf, count, &enabled);
+	if (ret)
+		return ret;
+
+	ret = tsp->ops->collection_configure(tsp->ph, SCMI_TLM_GRP_INVALID, true,
+					     &enabled, NULL, &mode);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct file_operations tlm_enable_fops = {
+	.open = nonseekable_open,
+	.read = scmi_tlm_enable_read,
+	.write = scmi_tlm_enable_write,
+};
+
+static ssize_t
+scmi_tlm_intrv_discrete_read(struct file *filp, char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	bool discrete;
+	char o_buf[2];
+
+	discrete = !IS_GROUP(tlmi->cls->flags) ?
+		tlmi->info->intervals->discrete : tlmi->grp->intervals->discrete;
+
+	o_buf[0] = discrete ? 'Y' : 'N';
+	o_buf[1] = '\n';
+
+	return simple_read_from_buffer(buf, count, ppos, o_buf, 2);
+}
+
+static const struct file_operations intrv_discrete_fops = {
+	.open = nonseekable_open,
+	.read = scmi_tlm_intrv_discrete_read,
+};
+
+static ssize_t
+scmi_tlm_reset_write(struct file *filp, const char __user *buf, size_t count,
+		     loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	int ret;
+
+	ret = tlmi->tsp->ops->reset(tlmi->tsp->ph);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct file_operations reset_fops = {
+	.open = nonseekable_open,
+	.write = scmi_tlm_reset_write,
+};
+
+static int sa_u32_get(void *data, u64 *val)
+{
+	*val = *(u32 *)data;
+	return 0;
+}
+
+static int sa_u32_set(void *data, u64 val)
+{
+	*(u32 *)data = val;
+	return 0;
+}
+
+static int sa_u32_open(struct inode *ino, struct file *filp)
+{
+	return simple_attr_open(ino, filp, sa_u32_get, sa_u32_set, "%u\n");
+}
+
+static int sa_s32_open(struct inode *ino, struct file *filp)
+{
+	return simple_attr_open(ino, filp, sa_u32_get, sa_u32_set, "%d\n");
+}
+
+static int sa_x32_open(struct inode *ino, struct file *filp)
+{
+	return simple_attr_open(ino, filp, sa_u32_get, sa_u32_set, "0x%X\n");
+}
+
+static const struct file_operations sa_x32_ro_fops = {
+	.open = sa_x32_open,
+	.read = simple_attr_read,
+	.release = simple_attr_release,
+};
+
+static const struct file_operations sa_u32_ro_fops = {
+	.open = sa_u32_open,
+	.read = simple_attr_read,
+	.release = simple_attr_release,
+};
+
+static const struct file_operations sa_s32_ro_fops = {
+	.open = sa_s32_open,
+	.read = simple_attr_read,
+	.release = simple_attr_release,
+};
+
+static ssize_t
+scmi_de_impl_version_read(struct file *filp, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	struct scmi_tlm_buffer *data = filp->private_data;
+
+	if (!data)
+		return 0;
+
+	if (!data->used)
+		data->used = scnprintf(data->buf, SCMI_TLM_MAX_BUF_SZ,
+				       "%pUL\n", tlmi->info->base.de_impl_version);
+
+	return simple_read_from_buffer(buf, count, ppos, data->buf, data->used);
+}
+
+static const struct file_operations de_impl_vers_fops = {
+	.open = scmi_tlm_open,
+	.read = scmi_de_impl_version_read,
+	.release = scmi_tlm_release,
+};
+
+static ssize_t scmi_string_read(struct file *filp, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_priv *tp = filp->private_data;
+
+	if (!tp->buf) {
+		struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+		const char *str = tlmi->priv;
+
+		tp->buf = kasprintf(GFP_KERNEL, "%s\n", str);
+		if (!tp->buf)
+			return -ENOMEM;
+
+		tp->buf_len = strlen(tp->buf) + 1;
+	}
+
+	return simple_read_from_buffer(buf, count, ppos, tp->buf, tp->buf_len);
+}
+
+static int scmi_tlm_priv_open(struct inode *ino, struct file *filp)
+{
+	return __scmi_tlm_generic_open(ino, filp, NULL);
+}
+
+static const struct file_operations string_ro_fops = {
+	.open = scmi_tlm_priv_open,
+	.read = scmi_string_read,
+	.release = scmi_tlm_priv_release,
+};
+
+static ssize_t scmi_available_interv_read(struct file *filp, char __user *buf,
+					  size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_priv *tp = filp->private_data;
+
+	if (!tp->buf) {
+		struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+		struct scmi_tlm_intervals *intervals;
+		int len = 0;
+
+		intervals = !IS_GROUP(tlmi->cls->flags) ?
+			tlmi->info->intervals : tlmi->grp->intervals;
+		tp->buf_len = intervals->num * MAX_AVAILABLE_INTERV_CHAR_LENGTH;
+		tp->buf = kzalloc(tp->buf_len, GFP_KERNEL);
+		if (!tp->buf)
+			return -ENOMEM;
+
+		for (int i = 0; i < intervals->num; i++) {
+			u32 ivl;
+
+			ivl = intervals->update_intervals[i];
+			len += scnprintf(tp->buf + len, tp->buf_len - len,
+					 "%u,%d ",
+					 SCMI_TLM_GET_UPDATE_INTERVAL_SECS(ivl),
+					 SCMI_TLM_GET_UPDATE_INTERVAL_EXP(ivl));
+		}
+		tp->buf[len - 1] = '\n';
+	}
+
+	return simple_read_from_buffer(buf, count, ppos, tp->buf, tp->buf_len);
+}
+
+static const struct file_operations available_interv_fops = {
+	.open = scmi_tlm_priv_open,
+	.read = scmi_available_interv_read,
+	.release = scmi_tlm_priv_release,
+};
+
+static const struct scmi_tlm_class tlm_tops[] = {
+	TLM_ANON_CLASS("all_des_enable", TLM_IS_STATE, 0600, &all_des_fops, NULL),
+	TLM_ANON_CLASS("all_des_tstamp_enable", 0, 0600, &all_des_fops, NULL),
+	TLM_ANON_CLASS("current_update_interval_ms", 0, 0600, &current_interval_fops, NULL),
+	TLM_ANON_CLASS("intervals_discrete", 0, 0400, &intrv_discrete_fops, NULL),
+	TLM_ANON_CLASS("available_update_intervals_ms", 0, 0400,
+		       &available_interv_fops, NULL),
+	TLM_ANON_CLASS("de_implementation_version", 0, 0400, &de_impl_vers_fops, NULL),
+	TLM_ANON_CLASS("tlm_enable", 0, 0600, &tlm_enable_fops, NULL),
+	TLM_ANON_CLASS(NULL, 0, 0, NULL, NULL),
+};
+
+DEFINE_TLM_CLASS(reset_tlmo, "reset", 0, 0200, &reset_fops, NULL);
+
+static const struct inode_operations tlm_dir_inode_ops = {
+	.lookup = simple_lookup,
+};
+
+static const struct inode_operations tlm_file_inode_ops = { };
+
+DEFINE_TLM_CLASS(des_dir_cls, "des", 0, 0700, NULL, &tlm_dir_inode_ops);
+DEFINE_TLM_CLASS(name_tlmo, "name", 0, 0400, &string_ro_fops, NULL);
+DEFINE_TLM_CLASS(ena_tlmo, "enable", TLM_IS_STATE, 0600, &obj_enable_fops, NULL);
+DEFINE_TLM_CLASS(tstamp_ena_tlmo, "tstamp_enable", 0, 0600, &obj_enable_fops, NULL);
+DEFINE_TLM_CLASS(type_tlmo, "type", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(unit_tlmo, "unit", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(unit_exp_tlmo, "unit_exp", 0, 0400, &sa_s32_ro_fops, NULL);
+DEFINE_TLM_CLASS(instance_id_tlmo, "instance_id", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(compo_type_tlmo, "compo_type", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(compo_inst_id_tlmo, "compo_instance_id", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(tstamp_exp_tlmo, "tstamp_exp", 0, 0400, &sa_s32_ro_fops, NULL);
+DEFINE_TLM_CLASS(persistent_tlmo, "persistent", 0, 0400, &sa_u32_ro_fops, NULL);
+DEFINE_TLM_CLASS(value_tlmo, "value", 0, 0400, &de_read_fops, NULL);
+
+static struct scmi_tlm_inode *
+scmi_tlm_inode_create(const struct scmi_tlm_setup *tsp,
+		      const struct scmi_tlm_class *cls,
+		      struct scmi_tlm_inode *parent, const void *priv)
+{
+	struct scmi_tlm_inode *tlmi;
+
+	tlmi = devm_kzalloc(tsp->dev, sizeof(*tlmi), GFP_KERNEL);
+	if (!tlmi)
+		return NULL;
+
+	tlmi->cls = cls;
+	tlmi->parent = parent;
+	tlmi->tsp = tsp;
+	tlmi->priv = priv;
+
+	return tlmi;
+}
+
+static int scmi_telemetry_des_initialize(struct device *dev,
+					 struct scmi_tlm_instance *ti)
+{
+	struct scmi_tlm_setup *tsp = ti->tsp;
+	struct scmi_tlm_inode *des_top_inode;
+
+	des_top_inode = TLM_INODE_SETUP(ti, tsp, &des_dir_cls, NULL, NULL);
+
+	for (int i = 0; i < ti->info->base.num_des; i++) {
+		const struct scmi_telemetry_de *de = ti->info->des[i];
+		struct scmi_tlm_de_info *dei = de->info;
+		struct scmi_tlm_inode *de_dir_inode;
+		struct scmi_tlm_class *de_tlm_cls;
+
+		de_tlm_cls = devm_kzalloc(tsp->dev, sizeof(*de_tlm_cls), GFP_KERNEL);
+		if (!de_tlm_cls)
+			return -ENOMEM;
+
+		de_tlm_cls->name = devm_kasprintf(dev, GFP_KERNEL, "0x%04X", dei->id);
+		if (!de_tlm_cls->name)
+			return -ENOMEM;
+
+		de_tlm_cls->mode = 0700;
+		de_tlm_cls->i_op = &tlm_dir_inode_ops;
+		de_dir_inode = TLM_INODE_SETUP(ti, tsp, de_tlm_cls, des_top_inode, de);
+
+		if (de->name_support)
+			TLM_INODE_SETUP(ti, tsp, &name_tlmo, de_dir_inode, dei->name);
+
+		TLM_INODE_SETUP(ti, tsp, &ena_tlmo, de_dir_inode, de);
+		if (de->tstamp_support) {
+			TLM_INODE_SETUP(ti, tsp, &tstamp_ena_tlmo, de_dir_inode, de);
+			TLM_INODE_SETUP(ti, tsp, &tstamp_exp_tlmo, de_dir_inode,
+					&dei->tstamp_exp);
+		}
+
+		TLM_INODE_SETUP(ti, tsp, &type_tlmo, de_dir_inode, &dei->type);
+		TLM_INODE_SETUP(ti, tsp, &unit_tlmo, de_dir_inode, &dei->unit);
+		TLM_INODE_SETUP(ti, tsp, &unit_exp_tlmo, de_dir_inode,
+				&dei->unit_exp);
+		TLM_INODE_SETUP(ti, tsp, &instance_id_tlmo, de_dir_inode,
+				&dei->instance_id);
+		TLM_INODE_SETUP(ti, tsp, &compo_type_tlmo, de_dir_inode,
+				&dei->compo_type);
+		TLM_INODE_SETUP(ti, tsp, &compo_inst_id_tlmo, de_dir_inode,
+				&dei->compo_instance_id);
+		TLM_INODE_SETUP(ti, tsp, &persistent_tlmo, de_dir_inode,
+				&dei->persistent);
+
+		TLM_INODE_SETUP(ti, tsp, &value_tlmo, de_dir_inode, de);
+	}
+
+	dev_info(dev, "Found %d Telemetry DE resources.\n", ti->info->base.num_des);
+
+	return 0;
+}
+
+DEFINE_TLM_CLASS(version_tlmo, "version", 0, 0400, &sa_x32_ro_fops, NULL);
+
+static int scmi_tlm_bulk_on_demand(const struct scmi_tlm_setup *tsp,
+				   int res_id, int *num_samples,
+				   struct scmi_telemetry_de_sample *samples)
+{
+	return tsp->ops->des_bulk_read(tsp->ph, res_id, num_samples, samples);
+}
+
+static int scmi_tlm_data_open(struct inode *ino, struct file *filp)
+{
+	return __scmi_tlm_generic_open(ino, filp, scmi_tlm_bulk_on_demand);
+}
+
+static int scmi_tlm_buffer_fill(struct device *dev, char *buf, size_t size,
+				int *len, int num,
+				struct scmi_telemetry_de_sample *samples)
+{
+	int idx, bytes = 0;
+
+	/* Loop till there space for the next line */
+	for (idx = 0; idx < num && size - bytes >= MAX_BULK_LINE_CHAR_LENGTH; idx++) {
+		bytes += scnprintf(buf + bytes, size - bytes,
+				   "0x%04X %llu %016llX\n", samples[idx].id,
+				   samples[idx].tstamp, samples[idx].val);
+	}
+
+	if (idx < num) {
+		dev_err(dev, "Bulk buffer truncated !\n");
+		return -ENOSPC;
+	}
+
+	if (len)
+		*len = bytes;
+
+	return 0;
+}
+
+static int scmi_tlm_bulk_buffer_allocate_and_fill(struct scmi_tlm_inode *tlmi,
+						  struct scmi_tlm_priv *tp)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	const struct scmi_tlm_class *cls = tlmi->cls;
+	struct scmi_telemetry_de_sample *samples;
+	bool is_group = IS_GROUP(cls->flags);
+	int ret, num_samples, res_id;
+
+	num_samples = !is_group ? tlmi->info->base.num_des :
+		tlmi->grp->info->num_des;
+	tp->buf_sz = num_samples * MAX_BULK_LINE_CHAR_LENGTH;
+	tp->buf = kzalloc(tp->buf_sz, GFP_KERNEL);
+	if (!tp->buf)
+		return -ENOMEM;
+
+	res_id = is_group ? tlmi->grp->info->id : SCMI_TLM_GRP_INVALID;
+	samples = kcalloc(num_samples, sizeof(*samples), GFP_KERNEL);
+	if (!samples) {
+		kfree(tp->buf);
+		return -ENOMEM;
+	}
+
+	ret = tp->bulk_retrieve(tsp, res_id, &num_samples, samples);
+	if (ret) {
+		kfree(samples);
+		kfree(tp->buf);
+		return ret;
+	}
+
+	ret = scmi_tlm_buffer_fill(tsp->dev, tp->buf, tp->buf_sz, &tp->buf_len,
+				   num_samples, samples);
+	kfree(samples);
+
+	return ret;
+}
+
+static ssize_t scmi_tlm_generic_data_read(struct file *filp, char __user *buf,
+					  size_t count, loff_t *ppos)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	struct scmi_tlm_priv *tp = filp->private_data;
+	int ret;
+
+	if (!tp->buf) {
+		ret = scmi_tlm_bulk_buffer_allocate_and_fill(tlmi, tp);
+		if (ret)
+			return ret;
+	}
+
+	return simple_read_from_buffer(buf, count, ppos, tp->buf, tp->buf_len);
+}
+
+static const struct file_operations scmi_tlm_data_fops = {
+	.owner = THIS_MODULE,
+	.open = scmi_tlm_data_open,
+	.read = scmi_tlm_generic_data_read,
+	.release = scmi_tlm_priv_release,
+};
+
+DEFINE_TLM_CLASS(data_tlmo, "des_bulk_read", 0, 0400, &scmi_tlm_data_fops, NULL);
+
+static int scmi_tlm_bulk_single_read(const struct scmi_tlm_setup *tsp,
+				     int res_id, int *num_samples,
+				     struct scmi_telemetry_de_sample *samples)
+{
+	return tsp->ops->des_sample_get(tsp->ph, res_id, num_samples, samples);
+}
+
+static int scmi_tlm_single_read_open(struct inode *ino, struct file *filp)
+{
+	return __scmi_tlm_generic_open(ino, filp, scmi_tlm_bulk_single_read);
+}
+
+/* TODO
+ * Could be better with a .poll fops since single sample
+ * reads trigger an asynchronous request.
+ */
+static const struct file_operations scmi_tlm_single_sample_fops = {
+	.owner = THIS_MODULE,
+	.open = scmi_tlm_single_read_open,
+	.read = scmi_tlm_generic_data_read,
+	.release = scmi_tlm_priv_release,
+};
+
+DEFINE_TLM_CLASS(single_sample_tlmo, "des_single_sample_read", 0, 0400,
+		 &scmi_tlm_single_sample_fops, NULL);
+
+static const struct scmi_tlm_class tlm_grps[] = {
+	TLM_ANON_CLASS("enable", TLM_IS_STATE | TLM_IS_GROUP, 0600, &obj_enable_fops, NULL),
+	TLM_ANON_CLASS("tstamp_enable", TLM_IS_GROUP, 0600, &obj_enable_fops, NULL),
+	TLM_ANON_CLASS(NULL, 0, 0, NULL, NULL),
+};
+
+DEFINE_TLM_CLASS(grp_data_tlmo, "des_bulk_read", TLM_IS_GROUP, 0400,
+		 &scmi_tlm_data_fops, NULL);
+
+DEFINE_TLM_CLASS(groups_dir_cls, "groups", 0, 0700, NULL, &tlm_dir_inode_ops);
+
+DEFINE_TLM_CLASS(grp_single_sample_tlmo, "des_single_sample_read", TLM_IS_GROUP,
+		 0400, &scmi_tlm_single_sample_fops, NULL);
+
+DEFINE_TLM_CLASS(grp_composing_des_tlmo, "composing_des", TLM_IS_GROUP, 0400,
+		 &string_ro_fops, NULL);
+
+DEFINE_TLM_CLASS(grp_current_interval_tlmo, "current_update_interval_ms",
+		 TLM_IS_GROUP, 0600, &current_interval_fops, NULL);
+
+DEFINE_TLM_CLASS(grp_available_interval_tlmo, "available_update_intervals_ms",
+		 TLM_IS_GROUP, 0400, &available_interv_fops, NULL);
+
+DEFINE_TLM_CLASS(grp_intervals_discrete_tlmo, "intervals_discrete",
+		 TLM_IS_GROUP, 0400, &intrv_discrete_fops, NULL);
+
+static int scmi_telemetry_groups_initialize(struct device *dev,
+					    struct scmi_tlm_instance *ti)
+{
+	struct scmi_tlm_setup *tsp = ti->tsp;
+	struct scmi_tlm_inode *groups_top_inode;
+
+	if (ti->info->base.num_groups == 0)
+		return 0;
+
+	groups_top_inode = TLM_INODE_SETUP(ti, tsp, &groups_dir_cls, NULL, NULL);
+
+	for (int i = 0; i < ti->info->base.num_groups; i++) {
+		const struct scmi_telemetry_group *grp = &ti->info->groups[i];
+		struct scmi_tlm_class *grp_tlm_cls;
+		struct scmi_tlm_inode *grp_dir_inode;
+
+		grp_tlm_cls = devm_kzalloc(tsp->dev, sizeof(*grp_tlm_cls), GFP_KERNEL);
+		if (!grp_tlm_cls)
+			return -ENOMEM;
+
+		grp_tlm_cls->name = devm_kasprintf(dev, GFP_KERNEL, "%u", grp->info->id);
+		if (!grp_tlm_cls->name)
+			return -ENOMEM;
+
+		grp_tlm_cls->mode = 0700;
+		grp_tlm_cls->i_op = &tlm_dir_inode_ops;
+
+		grp_dir_inode = TLM_INODE_SETUP(ti, tsp, grp_tlm_cls,
+						groups_top_inode, grp);
+
+		for (const struct scmi_tlm_class *gto = tlm_grps; gto->name; gto++)
+			TLM_INODE_SETUP(ti, tsp, gto, grp_dir_inode, grp);
+
+		TLM_INODE_SETUP(ti, tsp, &grp_composing_des_tlmo, grp_dir_inode,
+				grp->des_str);
+
+		TLM_INODE_SETUP(ti, tsp, &grp_data_tlmo, grp_dir_inode, grp);
+		TLM_INODE_SETUP(ti, tsp, &grp_single_sample_tlmo, grp_dir_inode, grp);
+
+		if (ti->info->per_group_config_support) {
+			TLM_INODE_SETUP(ti, tsp, &grp_current_interval_tlmo,
+					grp_dir_inode, grp);
+			TLM_INODE_SETUP(ti, tsp, &grp_available_interval_tlmo,
+					grp_dir_inode, grp);
+			TLM_INODE_SETUP(ti, tsp, &grp_intervals_discrete_tlmo,
+					grp_dir_inode, grp);
+		}
+	}
+
+	dev_info(dev, "Found %d Telemetry GROUPS resources.\n",
+		 ti->info->base.num_groups);
+
+	return 0;
+}
+
+static int scmi_tlm_root_instance_initialize(struct device *dev,
+					     struct scmi_tlm_instance *ti)
+{
+	struct scmi_tlm_setup *tsp = ti->tsp;
+
+	/*
+	 * Allocate space for all possible nodes, i.e. in order:
+	 *  - top level nodes
+	 *  - all DE subdirs contained in des/
+	 *  - all DE proeperties files inside each 0xNNNN/ DE subdir
+	 *  - all GRPS subdirs contained in groups/
+	 *  - all GRPS proeperties files inside each <N>/ GRP subdir
+	 */
+	ti->max_nodes = TOP_NODES_NUM + ti->info->base.num_des +
+		NODES_PER_DE_NUM * ti->info->base.num_des +
+		ti->info->base.num_groups +
+		NODES_PER_GRP_NUM * ti->info->base.num_groups;
+	ti->all_nodes = devm_kcalloc(tsp->dev, ti->max_nodes,
+				     sizeof(*ti->all_nodes), GFP_KERNEL);
+	if (!ti->all_nodes)
+		return -ENOMEM;
+
+	scnprintf(ti->name, MAX_INST_NAME, "tlm_%d", ti->id);
+
+	/* Allocate top instance node */
+	ti->top_cls.name = ti->name;
+	ti->top_cls.mode = 0755;
+	/*
+	 * Do NOT register the top_node root in all_nodes[] since it is
+	 * treated differently at mount time
+	 */
+	ti->top_inode = scmi_tlm_inode_create(tsp, &ti->top_cls, NULL, NULL);
+
+	for (const struct scmi_tlm_class *tlmo = tlm_tops; tlmo->name; tlmo++)
+		TLM_INODE_SETUP(ti, tsp, tlmo, NULL, ti->info);
+
+	if (ti->info->reset_support)
+		TLM_INODE_SETUP(ti, tsp, &reset_tlmo, NULL, NULL);
+
+	TLM_INODE_SETUP(ti, tsp, &version_tlmo, NULL, &ti->info->base.version);
+	TLM_INODE_SETUP(ti, tsp, &data_tlmo, NULL, ti->info);
+	TLM_INODE_SETUP(ti, tsp, &single_sample_tlmo, NULL, ti->info);
+
+	return 0;
+}
+
+static struct scmi_tlm_instance *scmi_tlm_init(struct scmi_tlm_setup *tsp,
+					       int instance_id)
+{
+	struct device *dev = tsp->dev;
+	struct scmi_tlm_instance *ti;
+	int ret;
+
+	ti = devm_kzalloc(dev, sizeof(*ti), GFP_KERNEL);
+	if (!ti)
+		return ERR_PTR(-ENOMEM);
+
+	ti->info = tsp->ops->info_get(tsp->ph);
+	if (!ti->info) {
+		dev_err(dev, "invalid Telemetry info !\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ti->id = instance_id;
+	ti->tsp = tsp;
+
+	ret = scmi_tlm_root_instance_initialize(dev, ti);
+	if (ret)
+		return ERR_PTR(ret);
+
+	ret = scmi_telemetry_des_initialize(dev, ti);
+	if (ret)
+		return ERR_PTR(ret);
+
+	ret = scmi_telemetry_groups_initialize(dev, ti);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return ti;
+}
+
+static int scmi_telemetry_probe(struct scmi_device *sdev)
+{
+	const struct scmi_handle *handle = sdev->handle;
+	struct scmi_protocol_handle *ph;
+	struct device *dev = &sdev->dev;
+	struct scmi_tlm_instance *ti;
+	struct scmi_tlm_setup *tsp;
+	const void *ops;
+
+	if (!handle)
+		return -ENODEV;
+
+	ops = handle->devm_protocol_get(sdev, sdev->protocol_id, &ph);
+	if (IS_ERR(ops))
+		return dev_err_probe(dev, PTR_ERR(ops),
+				     "Cannot access protocol:0x%X\n",
+				     sdev->protocol_id);
+
+	tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL);
+	if (!tsp)
+		return -ENOMEM;
+
+	tsp->dev = dev;
+	tsp->ops = ops;
+	tsp->ph = ph;
+
+	ti = scmi_tlm_init(tsp, atomic_fetch_inc(&scmi_tlm_instance_count));
+	if (IS_ERR(ti))
+		return PTR_ERR(ti);
+
+	mutex_lock(&scmi_tlm_mtx);
+	list_add(&ti->node, &scmi_telemetry_instances);
+	if (scmi_tlm_sb) {
+		int ret;
+
+		/*
+		 * If the file system was already mounted by the time this
+		 * instance was probed, register explicitly, since the list
+		 * has been scanned already.
+		 */
+		mutex_unlock(&scmi_tlm_mtx);
+		ret = scmi_telemetry_instance_register(scmi_tlm_sb, ti);
+		if (ret)
+			return ret;
+		mutex_lock(&scmi_tlm_mtx);
+	}
+	mutex_unlock(&scmi_tlm_mtx);
+
+	dev_set_drvdata(&sdev->dev, ti);
+
+	return 0;
+}
+
+static void scmi_telemetry_remove(struct scmi_device *sdev)
+{
+	struct device *dev = &sdev->dev;
+	struct scmi_tlm_instance *ti;
+	bool enabled = false;
+	int ret;
+
+	ti = dev_get_drvdata(&sdev->dev);
+
+	/* Stop SCMI Telemetry collection */
+	ret = ti->tsp->ops->collection_configure(ti->tsp->ph,
+						 SCMI_TLM_GRP_INVALID, true,
+						 &enabled, NULL, NULL);
+	if (ret)
+		dev_warn(dev, "Failed to stop Telemetry collection\n");
+
+	list_del(&ti->node);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_TELEMETRY, "telemetry" },
+	{ },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_telemetry_driver = {
+	.name = "scmi-telemetry-driver",
+	.probe = scmi_telemetry_probe,
+	.remove = scmi_telemetry_remove,
+	.id_table = scmi_id_table,
+};
+
+static const struct super_operations tlm_sops = {
+	.statfs = simple_statfs,
+	.drop_inode = generic_delete_inode,
+};
+
+static struct inode *
+scmi_tlm_inode_initialize(struct super_block *sb, umode_t mode,
+			  struct scmi_tlm_inode *tlmi)
+{
+	struct inode *inode;
+
+	if (!tlmi)
+		return NULL;
+
+	inode = &tlmi->vfs_inode;
+	if (unlikely(inode_init_always(sb, inode)))
+		return NULL;
+
+	inode_sb_list_add(inode);
+	inode->i_ino = get_next_ino();
+	inode_init_owner(&nop_mnt_idmap, inode, NULL, mode);
+	simple_inode_init_ts(inode);
+
+	if (S_ISDIR(mode)) {
+		inode->i_op = &tlm_dir_inode_ops;
+		inode->i_fop = &simple_dir_operations;
+	} else if (S_ISREG(mode)) {
+		inode->i_op = &tlm_file_inode_ops;
+		inode->i_fop = tlmi ? tlmi->cls->f_op : NULL;
+	}
+
+	inode->i_private = (void *)tlmi->priv;
+
+	return inode;
+}
+
+static struct dentry *
+scmi_tlm_node_add(struct super_block *sb, struct dentry *parent,
+		  const char *name, umode_t mode, struct scmi_tlm_inode *tlmi)
+{
+	struct inode *ino;
+	struct dentry *dentry;
+
+	ino = scmi_tlm_inode_initialize(sb, mode, tlmi);
+	if (!ino)
+		return ERR_PTR(-ENOMEM);
+
+	dentry = d_alloc_name(parent, name);
+	if (!dentry)
+		return ERR_PTR(-ENOMEM);
+
+	tlmi->dentry = dentry;
+	d_add(dentry, ino);
+
+	return dentry;
+}
+
+static int scmi_telemetry_instance_register(struct super_block *sb,
+					    struct scmi_tlm_instance *ti)
+{
+	struct dentry *top;
+
+	/* AT first create instance top dir ... */
+	top = scmi_tlm_node_add(sb, sb->s_root, ti->top_cls.name,
+				S_IFDIR | ti->top_cls.mode, ti->top_inode);
+	if (IS_ERR(top))
+		return PTR_ERR(top);
+
+	/*
+	 * Scan the array of tlm_inode pre-initialized with SCMI Telemetry
+	 * discovered entities and map it into the filesystem .
+	 */
+	for (int i = 0; i < ti->num_nodes; i++) {
+		struct scmi_tlm_inode *tlmi = ti->all_nodes[i];
+		struct dentry *dentry, *parent;
+		umode_t mode;
+
+		/* Check sanity of node tree */
+		if (WARN_ON_ONCE(!tlmi))
+			continue;
+
+		parent = !tlmi->parent ? top : tlmi->parent->dentry;
+		mode = (tlmi->cls->f_op ? S_IFREG : S_IFDIR) | tlmi->cls->mode;
+		dentry = scmi_tlm_node_add(sb, parent, tlmi->cls->name, mode, tlmi);
+		if (IS_ERR(dentry))
+			return PTR_ERR(dentry);
+	}
+
+	return 0;
+}
+
+static struct scmi_tlm_inode root_tlm;
+
+static int tlm_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *root_inode;
+	struct dentry *root_dentry;
+	struct scmi_tlm_instance *ti;
+
+	sb->s_magic = TLM_FS_MAGIC;
+	sb->s_op = &tlm_sops;
+
+	/* create root inode (directory) */
+	root_inode = scmi_tlm_inode_initialize(sb, S_IFDIR | 0755, &root_tlm);
+	if (!root_inode)
+		return -ENOMEM;
+
+	root_dentry = d_make_root(root_inode);
+	if (!root_dentry)
+		return -ENOMEM;
+
+	sb->s_root = root_dentry;
+	list_for_each_entry(ti, &scmi_telemetry_instances, node) {
+		int ret;
+
+		ret = scmi_telemetry_instance_register(sb, ti);
+		if (ret)
+			return ret;
+	}
+
+	guard(mutex)(&scmi_tlm_mtx);
+	if (!scmi_tlm_sb)
+		scmi_tlm_sb = sb;
+
+	return 0;
+}
+
+static struct dentry *tlm_mount(struct file_system_type *fs_type, int flags,
+				const char *dev_name, void *data)
+{
+	return mount_nodev(fs_type, flags, data, tlm_fill_super);
+}
+
+static void tlm_kill_sb(struct super_block *sb)
+{
+	kill_litter_super(sb);
+}
+
+//XXX Move to new fs_context-based MOUNT process !!! (see debugfs_parse_param)
+static struct file_system_type scmi_telemetry_fs = {
+	.owner = THIS_MODULE,
+	.name = TLM_FS_NAME,
+	.mount = tlm_mount,
+	.kill_sb = tlm_kill_sb,
+	.fs_flags = 0,
+};
+
+static int __init scmi_telemetry_init(void)
+{
+	int ret;
+
+	ret = scmi_register(&scmi_telemetry_driver);
+	if (ret)
+		return ret;
+
+	ret = sysfs_create_mount_point(fs_kobj, TLM_FS_MNT);
+	if (ret && ret != -EEXIST) {
+		scmi_unregister(&scmi_telemetry_driver);
+		return ret;
+	}
+
+	ret = register_filesystem(&scmi_telemetry_fs);
+	if (ret) {
+		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
+		scmi_unregister(&scmi_telemetry_driver);
+	}
+
+	return ret;
+}
+module_init(scmi_telemetry_init);
+
+static void __exit scmi_telemetry_exit(void)
+{
+	int ret;
+
+	ret = unregister_filesystem(&scmi_telemetry_fs);
+	if (!ret)
+		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
+	else
+		pr_err("Failed to unregister %s\n", TLM_FS_NAME);
+
+	scmi_unregister(&scmi_telemetry_driver);
+}
+module_exit(scmi_telemetry_exit);
+
+MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI Telemetry Driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (5 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-10-20 16:30   ` Jonathan Cameron
  2025-09-25 20:35 ` [PATCH 08/10] firmware: arm_scmi: Add Telemetry components view Cristian Marussi
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Extend the filesystem based interface with special 'control' file that can
be used to configure and retrieve SCMI Telemetry data in binary form using
the alternative ioctls-based ABI described in uapi/linux/scmi.h.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 .../firmware/arm_scmi/scmi_system_telemetry.c | 402 ++++++++++++++++++
 1 file changed, 402 insertions(+)

diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
index 2fec465b0f33..f591aad10302 100644
--- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -21,6 +21,8 @@
 #include <linux/string.h>
 #include <linux/uaccess.h>
 
+#include <uapi/linux/scmi.h>
+
 #define TLM_FS_MAGIC		0x75C01C80
 #define TLM_FS_NAME		"stlmfs"
 #define TLM_FS_MNT		"arm_telemetry"
@@ -953,6 +955,404 @@ DEFINE_TLM_CLASS(grp_available_interval_tlmo, "available_update_intervals_ms",
 DEFINE_TLM_CLASS(grp_intervals_discrete_tlmo, "intervals_discrete",
 		 TLM_IS_GROUP, 0400, &intrv_discrete_fops, NULL);
 
+static long
+scmi_tlm_info_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_telemetry_info *info = tlmi->priv;
+	void * __user uptr = (void * __user)arg;
+
+	if (copy_to_user(uptr, &info->base, sizeof(info->base)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_intervals_get_ioctl(const struct scmi_tlm_inode *tlmi,
+			     unsigned long arg, bool is_group)
+{
+	struct scmi_tlm_intervals ivs, *tlm_ivs;
+	void * __user uptr = (void * __user)arg;
+
+	if (copy_from_user(&ivs, uptr, sizeof(ivs)))
+		return -EFAULT;
+
+	if (!is_group) {
+		const struct scmi_telemetry_info *info = tlmi->priv;
+
+		tlm_ivs = info->intervals;
+	} else {
+		const struct scmi_telemetry_group *grp = tlmi->priv;
+
+		tlm_ivs = grp->intervals;
+	}
+
+	if (ivs.num != tlm_ivs->num)
+		return -EINVAL;
+
+	if (copy_to_user(uptr, tlm_ivs,
+			 sizeof(*tlm_ivs) + sizeof(u32) * ivs.num))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_de_config_set_ioctl(const struct scmi_tlm_inode *tlmi,
+			     unsigned long arg, bool all)
+{
+	const struct scmi_telemetry_info *info = tlmi->priv;
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_de_config tcfg = {};
+	int ret;
+
+	if (copy_from_user(&tcfg, uptr, sizeof(tcfg)))
+		return -EFAULT;
+
+	if (!all)
+		return tsp->ops->state_set(tsp->ph, false, tcfg.id,
+					   (bool *)&tcfg.enable,
+					   (bool *)&tcfg.t_enable);
+
+	for (int i = 0; i < info->base.num_des; i++) {
+		const struct scmi_telemetry_de *de = info->des[i];
+
+		ret = tsp->ops->state_set(tsp->ph, false, de->info->id,
+					  (bool *)&tcfg.enable,
+					  (bool *)&tcfg.t_enable);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static long
+scmi_tlm_de_config_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_de_config tcfg = {};
+	int ret;
+
+	if (copy_from_user(&tcfg, uptr, sizeof(tcfg)))
+		return -EFAULT;
+
+	ret = tsp->ops->state_get(tsp->ph, tcfg.id,
+				  (bool *)&tcfg.enable, (bool *)&tcfg.t_enable);
+	if (ret)
+		return ret;
+
+	if (copy_to_user(uptr, &tcfg, sizeof(tcfg)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_config_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg,
+			  bool is_group)
+{
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_config cfg;
+
+	if (!is_group) {
+		const struct scmi_telemetry_info *info = tlmi->priv;
+
+		cfg.enable = !!info->enabled;
+		cfg.current_update_interval = info->active_update_interval;
+	} else {
+		const struct scmi_telemetry_group *grp = tlmi->priv;
+
+		cfg.enable = !!grp->enabled;
+		cfg.t_enable = !!grp->tstamp_enabled;
+		cfg.current_update_interval = grp->active_update_interval;
+	}
+
+	if (copy_to_user(uptr, &cfg, sizeof(cfg)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_config_set_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg,
+			  bool is_group)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_config cfg = {};
+	bool grp_ignore;
+	int res_id;
+
+	if (copy_from_user(&cfg, uptr, sizeof(cfg)))
+		return -EFAULT;
+
+	if (!is_group) {
+		res_id = SCMI_TLM_GRP_INVALID;
+		grp_ignore = true;
+	} else {
+		const struct scmi_telemetry_group *grp = tlmi->priv;
+		int ret;
+
+		res_id = grp->info->id;
+		grp_ignore = false;
+		ret = tsp->ops->state_set(tsp->ph, true, res_id,
+					  (bool *)&cfg.enable,
+					  (bool *)&cfg.t_enable);
+		if (ret)
+			return ret;
+	}
+
+	return tsp->ops->collection_configure(tsp->ph, res_id, grp_ignore,
+					      (bool *)&cfg.enable,
+					      &cfg.current_update_interval,
+					      NULL);
+}
+
+static long
+scmi_tlm_de_info_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	const struct scmi_tlm_de_info *de_info;
+	struct scmi_tlm_de_info dei;
+
+	if (copy_from_user(&dei, uptr, sizeof(dei)))
+		return -EFAULT;
+
+	de_info = tsp->ops->de_info_get(tsp->ph, dei.id);
+	if (!de_info)
+		return -EINVAL;
+
+	if (copy_to_user(uptr, de_info, sizeof(*de_info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_des_list_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_telemetry_info *info = tlmi->priv;
+	void * __user uptr = (void * __user)arg;
+	int num_des = info->base.num_des;
+	struct scmi_tlm_des_list dsl;
+
+	if (copy_from_user(&dsl, uptr, sizeof(dsl)))
+		return -EFAULT;
+
+	if (dsl.num_des < num_des)
+		return -EINVAL;
+
+	if (copy_to_user(uptr, &num_des, sizeof(num_des)))
+		return -EFAULT;
+
+	if (copy_to_user(uptr + sizeof(num_des), info->des_store,
+			 info->base.num_des * sizeof(*info->des_store)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_de_value_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_de_sample sample;
+	int ret;
+
+	if (copy_from_user(&sample, uptr, sizeof(sample)))
+		return -EFAULT;
+
+	ret = tsp->ops->de_data_read(tsp->ph,
+				     (struct scmi_telemetry_de_sample *)&sample);
+	if (ret)
+		return ret;
+
+	if (copy_to_user(uptr, &sample, sizeof(sample)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_grp_info_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_telemetry_group *grp = tlmi->priv;
+	void * __user uptr = (void * __user)arg;
+
+	if (copy_to_user(uptr, grp->info, sizeof(*grp->info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_grp_desc_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_telemetry_group *grp = tlmi->priv;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_grp_desc grp_desc;
+	unsigned int num_des = grp->info->num_des;
+
+	if (copy_from_user(&grp_desc, uptr, sizeof(grp_desc)))
+		return -EFAULT;
+
+	if (grp_desc.num_des < num_des)
+		return -EINVAL;
+
+	if (copy_to_user(uptr, &num_des, sizeof(num_des)))
+		return -EFAULT;
+
+	if (copy_to_user(uptr + sizeof(num_des), grp->des,
+			 sizeof(*grp->des) * num_des))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long
+scmi_tlm_grps_list_get_ioctl(const struct scmi_tlm_inode *tlmi, unsigned long arg)
+{
+	const struct scmi_telemetry_info *info = tlmi->priv;
+	void * __user uptr = (void * __user)arg;
+	int num_grps = info->base.num_groups;
+	struct scmi_tlm_grps_list gsl;
+
+	if (copy_from_user(&gsl, uptr, sizeof(gsl)))
+		return -EFAULT;
+
+	if (gsl.num_grps < num_grps)
+		return -EINVAL;
+
+	if (copy_to_user(uptr, &num_grps, sizeof(num_grps)))
+		return -EFAULT;
+
+	if (copy_to_user(uptr + sizeof(num_grps), info->grps_store,
+			 info->base.num_groups * sizeof(*info->grps_store)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long scmi_tlm_des_read_ioctl(const struct scmi_tlm_inode *tlmi,
+				    unsigned long arg, bool single,
+				    bool is_group)
+{
+	const struct scmi_tlm_setup *tsp = tlmi->tsp;
+	void * __user uptr = (void * __user)arg;
+	struct scmi_tlm_data_read bulk, *bulk_ptr;
+	int ret, grp_id = SCMI_TLM_GRP_INVALID;
+
+	if (copy_from_user(&bulk, uptr, sizeof(bulk)))
+		return -EFAULT;
+
+	bulk_ptr = kzalloc(struct_size(bulk_ptr, samples, bulk.num_samples),
+			   GFP_KERNEL);
+	if (!bulk_ptr)
+		return -ENOMEM;
+
+	if (is_group) {
+		const struct scmi_telemetry_group *grp = tlmi->priv;
+
+		grp_id = grp->info->id;
+	}
+
+	bulk_ptr->num_samples = bulk.num_samples;
+	if (!single)
+		ret = tsp->ops->des_bulk_read(tsp->ph, grp_id,
+					      &bulk_ptr->num_samples,
+			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
+	else
+		ret = tsp->ops->des_sample_get(tsp->ph, grp_id,
+					       &bulk_ptr->num_samples,
+			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
+	if (ret)
+		goto out;
+
+	if (copy_to_user(uptr, bulk_ptr, sizeof(*bulk_ptr) +
+			 bulk_ptr->num_samples * sizeof(bulk_ptr->samples[0])))
+		ret = -EFAULT;
+
+out:
+	kfree(bulk_ptr);
+
+	return ret;
+}
+
+static long scmi_tlm_unlocked_ioctl(struct file *filp, unsigned int cmd,
+				    unsigned long arg)
+{
+	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
+	bool is_group = IS_GROUP(tlmi->cls->flags);
+
+	switch (cmd) {
+	case SCMI_TLM_GET_INFO:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_info_get_ioctl(tlmi, arg);
+	case SCMI_TLM_GET_CFG:
+		return scmi_tlm_config_get_ioctl(tlmi, arg, is_group);
+	case SCMI_TLM_SET_CFG:
+		return scmi_tlm_config_set_ioctl(tlmi, arg, is_group);
+	case SCMI_TLM_GET_INTRVS:
+		return scmi_tlm_intervals_get_ioctl(tlmi, arg, is_group);
+	case SCMI_TLM_GET_DE_CFG:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_de_config_get_ioctl(tlmi, arg);
+	case SCMI_TLM_SET_DE_CFG:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_de_config_set_ioctl(tlmi, arg, false);
+	case SCMI_TLM_GET_DE_INFO:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_de_info_get_ioctl(tlmi, arg);
+	case SCMI_TLM_GET_DE_LIST:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_des_list_get_ioctl(tlmi, arg);
+	case SCMI_TLM_GET_DE_VALUE:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_de_value_get_ioctl(tlmi, arg);
+	case SCMI_TLM_SET_ALL_CFG:
+		return scmi_tlm_de_config_set_ioctl(tlmi, arg, true);
+	case SCMI_TLM_GET_GRP_LIST:
+		if (is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_grps_list_get_ioctl(tlmi, arg);
+	case SCMI_TLM_GET_GRP_INFO:
+		if (!is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_grp_info_get_ioctl(tlmi, arg);
+	case SCMI_TLM_GET_GRP_DESC:
+		if (!is_group)
+			return -EOPNOTSUPP;
+		return scmi_tlm_grp_desc_get_ioctl(tlmi, arg);
+	case SCMI_TLM_SINGLE_SAMPLE:
+		return scmi_tlm_des_read_ioctl(tlmi, arg, true, is_group);
+	case SCMI_TLM_BULK_READ:
+		return scmi_tlm_des_read_ioctl(tlmi, arg, false, is_group);
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations scmi_tlm_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.open = nonseekable_open,
+	.unlocked_ioctl = scmi_tlm_unlocked_ioctl,
+};
+
+DEFINE_TLM_CLASS(ctrl_tlmo, "control", 0, 0600, &scmi_tlm_ctrl_fops, NULL);
+DEFINE_TLM_CLASS(grp_ctrl_tlmo, "control", TLM_IS_GROUP, 0600,
+		 &scmi_tlm_ctrl_fops, NULL);
+
 static int scmi_telemetry_groups_initialize(struct device *dev,
 					    struct scmi_tlm_instance *ti)
 {
@@ -989,6 +1389,7 @@ static int scmi_telemetry_groups_initialize(struct device *dev,
 		TLM_INODE_SETUP(ti, tsp, &grp_composing_des_tlmo, grp_dir_inode,
 				grp->des_str);
 
+		TLM_INODE_SETUP(ti, tsp, &grp_ctrl_tlmo, grp_dir_inode, grp);
 		TLM_INODE_SETUP(ti, tsp, &grp_data_tlmo, grp_dir_inode, grp);
 		TLM_INODE_SETUP(ti, tsp, &grp_single_sample_tlmo, grp_dir_inode, grp);
 
@@ -1050,6 +1451,7 @@ static int scmi_tlm_root_instance_initialize(struct device *dev,
 	TLM_INODE_SETUP(ti, tsp, &version_tlmo, NULL, &ti->info->base.version);
 	TLM_INODE_SETUP(ti, tsp, &data_tlmo, NULL, ti->info);
 	TLM_INODE_SETUP(ti, tsp, &single_sample_tlmo, NULL, ti->info);
+	TLM_INODE_SETUP(ti, tsp, &ctrl_tlmo, NULL, ti->info);
 
 	return 0;
 }
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 08/10] firmware: arm_scmi: Add Telemetry components view
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (6 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-25 20:35 ` [PATCH 09/10] include: trace: Add Telemetry trace events Cristian Marussi
  2025-09-25 20:35 ` [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces Cristian Marussi
  9 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add an alternative filesystem view for the discovered Data Events, where
the tree of DEs is laid out following the discovered topological order
instead of the existing flat layout.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 .../firmware/arm_scmi/scmi_system_telemetry.c | 730 ++++++++++++++++++
 1 file changed, 730 insertions(+)

diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
index f591aad10302..a4b6d23b211e 100644
--- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -189,6 +189,8 @@ struct scmi_tlm_inode {
  * @all_nodes: An array to keep track of all the initialized TLM nodes that
  *	       have been created as a result of the usual probe time SCMI
  *	       enumeration process.
+ * @des_nodes: An array to use as handy reference only to the set of nodes
+ *	       representing the DEs top directories.
  * @info: A handy reference to this instance SCMI Telemetry info data.
  *
  * The most notable field in this structure is the @all_nodes array, which
@@ -208,6 +210,7 @@ struct scmi_tlm_instance {
 	int max_nodes;
 	int num_nodes;
 	struct scmi_tlm_inode **all_nodes;
+	struct scmi_tlm_inode **des_nodes;
 	const struct scmi_telemetry_info *info;
 };
 
@@ -216,6 +219,526 @@ static int scmi_telemetry_instance_register(struct super_block *sb,
 
 static LIST_HEAD(scmi_telemetry_instances);
 
+#define TYPES_ARRAY_SZ		256
+
+static const char *compo_types[TYPES_ARRAY_SZ] = {
+	"unspec",
+	"cpu",
+	"cluster",
+	"gpu",
+	"npu",
+	"interconnnect",
+	"mem_cntrl",
+	"l1_cache",
+	"l2_cache",
+	"l3_cache",
+	"ll_cache",
+	"sys_cache",
+	"disp_cntrl",
+	"ipu",
+	"chiplet",
+	"package",
+	"soc",
+	"system",
+	"smcu",
+	"accel",
+	"battery",
+	"charger",
+	"pmic",
+	"board",
+	"memory",
+	"periph",
+	"periph_subc",
+	"lid",
+	"display",
+	"res_29",
+	"res_30",
+	"res_31",
+	"res_32",
+	"res_33",
+	"res_34",
+	"res_35",
+	"res_36",
+	"res_37",
+	"res_38",
+	"res_39",
+	"res_40",
+	"res_41",
+	"res_42",
+	"res_43",
+	"res_44",
+	"res_45",
+	"res_46",
+	"res_47",
+	"res_48",
+	"res_49",
+	"res_50",
+	"res_51",
+	"res_52",
+	"res_53",
+	"res_54",
+	"res_55",
+	"res_56",
+	"res_57",
+	"res_58",
+	"res_59",
+	"res_60",
+	"res_61",
+	"res_62",
+	"res_63",
+	"res_64",
+	"res_65",
+	"res_66",
+	"res_67",
+	"res_68",
+	"res_69",
+	"res_70",
+	"res_71",
+	"res_72",
+	"res_73",
+	"res_74",
+	"res_75",
+	"res_76",
+	"res_77",
+	"res_78",
+	"res_79",
+	"res_80",
+	"res_81",
+	"res_82",
+	"res_83",
+	"res_84",
+	"res_85",
+	"res_86",
+	"res_87",
+	"res_88",
+	"res_89",
+	"res_90",
+	"res_91",
+	"res_92",
+	"res_93",
+	"res_94",
+	"res_95",
+	"res_96",
+	"res_97",
+	"res_98",
+	"res_99",
+	"res_100",
+	"res_101",
+	"res_102",
+	"res_103",
+	"res_104",
+	"res_105",
+	"res_106",
+	"res_107",
+	"res_108",
+	"res_109",
+	"res_110",
+	"res_111",
+	"res_112",
+	"res_113",
+	"res_114",
+	"res_115",
+	"res_116",
+	"res_117",
+	"res_118",
+	"res_119",
+	"res_120",
+	"res_121",
+	"res_122",
+	"res_123",
+	"res_124",
+	"res_125",
+	"res_126",
+	"res_127",
+	"res_128",
+	"res_129",
+	"res_130",
+	"res_131",
+	"res_132",
+	"res_133",
+	"res_134",
+	"res_135",
+	"res_136",
+	"res_137",
+	"res_138",
+	"res_139",
+	"res_140",
+	"res_141",
+	"res_142",
+	"res_143",
+	"res_144",
+	"res_145",
+	"res_146",
+	"res_147",
+	"res_148",
+	"res_149",
+	"res_150",
+	"res_151",
+	"res_152",
+	"res_153",
+	"res_154",
+	"res_155",
+	"res_156",
+	"res_157",
+	"res_158",
+	"res_159",
+	"res_160",
+	"res_161",
+	"res_162",
+	"res_163",
+	"res_164",
+	"res_165",
+	"res_166",
+	"res_167",
+	"res_168",
+	"res_169",
+	"res_170",
+	"res_171",
+	"res_172",
+	"res_173",
+	"res_174",
+	"res_175",
+	"res_176",
+	"res_177",
+	"res_178",
+	"res_179",
+	"res_180",
+	"res_181",
+	"res_182",
+	"res_183",
+	"res_184",
+	"res_185",
+	"res_186",
+	"res_187",
+	"res_188",
+	"res_189",
+	"res_190",
+	"res_191",
+	"res_192",
+	"res_193",
+	"res_194",
+	"res_195",
+	"res_196",
+	"res_197",
+	"res_198",
+	"res_199",
+	"res_200",
+	"res_201",
+	"res_202",
+	"res_203",
+	"res_204",
+	"res_205",
+	"res_206",
+	"res_207",
+	"res_208",
+	"res_209",
+	"res_210",
+	"res_211",
+	"res_212",
+	"res_213",
+	"res_214",
+	"res_215",
+	"res_216",
+	"res_217",
+	"res_218",
+	"res_219",
+	"res_220",
+	"res_221",
+	"res_222",
+	"res_223",
+	"oem_224",
+	"oem_225",
+	"oem_226",
+	"oem_227",
+	"oem_228",
+	"oem_229",
+	"oem_230",
+	"oem_231",
+	"oem_232",
+	"oem_233",
+	"oem_234",
+	"oem_235",
+	"oem_236",
+	"oem_237",
+	"oem_238",
+	"oem_239",
+	"oem_240",
+	"oem_241",
+	"oem_242",
+	"oem_243",
+	"oem_244",
+	"oem_245",
+	"oem_246",
+	"oem_247",
+	"oem_248",
+	"oem_249",
+	"oem_250",
+	"oem_251",
+	"oem_252",
+	"oem_253",
+	"oem_254",
+	"oem_255",
+};
+
+static const char *unit_types[TYPES_ARRAY_SZ] = {
+	"none",
+	"unspec",
+	"celsius",
+	"fahrenheit",
+	"kelvin",
+	"volts",
+	"amps",
+	"watts",
+	"joules",
+	"coulombs",
+	"va",
+	"nits",
+	"lumens",
+	"lux",
+	"candelas",
+	"kpa",
+	"psi",
+	"newtons",
+	"cfm",
+	"rpm",
+	"hertz",
+	"seconds",
+	"minutes",
+	"hours",
+	"days",
+	"weeks",
+	"mils",
+	"inches",
+	"feet",
+	"cubic_inches",
+	"cubic_feet",
+	"meters",
+	"cubic_centimeters",
+	"cubic_meters",
+	"liters",
+	"fluid_ounces",
+	"radians",
+	"steradians",
+	"revolutions",
+	"cycles",
+	"gravities",
+	"ounces",
+	"pounds",
+	"foot_pounds",
+	"ounce_inches",
+	"gauss",
+	"gilberts",
+	"henries",
+	"farads",
+	"ohms",
+	"siemens",
+	"moles",
+	"becquerels",
+	"ppm",
+	"decibels",
+	"dba",
+	"dbc",
+	"grays",
+	"sieverts",
+	"color_temp_kelvin",
+	"bits",
+	"bytes",
+	"words",
+	"dwords",
+	"qwords",
+	"percentage",
+	"pascals",
+	"counts",
+	"grams",
+	"newton_meters",
+	"hits",
+	"misses",
+	"retries",
+	"overruns",
+	"underruns",
+	"collisions",
+	"packets",
+	"messages",
+	"chars",
+	"errors",
+	"corrected_err",
+	"uncorrectable_err",
+	"square_mils",
+	"square_inches",
+	"square_feet",
+	"square_centimeters",
+	"square_meters",
+	"radians_per_secs",
+	"beats_per_minute",
+	"meters_per_secs_squared",
+	"meters_per_secs",
+	"cubic_meter_per_secs",
+	"millimeters_mercury",
+	"radians_per_secs_squared",
+	"state",
+	"bps",
+	"res_96",
+	"res_97",
+	"res_98",
+	"res_99",
+	"res_100",
+	"res_101",
+	"res_102",
+	"res_103",
+	"res_104",
+	"res_105",
+	"res_106",
+	"res_107",
+	"res_108",
+	"res_109",
+	"res_110",
+	"res_111",
+	"res_112",
+	"res_113",
+	"res_114",
+	"res_115",
+	"res_116",
+	"res_117",
+	"res_118",
+	"res_119",
+	"res_120",
+	"res_121",
+	"res_122",
+	"res_123",
+	"res_124",
+	"res_125",
+	"res_126",
+	"res_127",
+	"res_128",
+	"res_129",
+	"res_130",
+	"res_131",
+	"res_132",
+	"res_133",
+	"res_134",
+	"res_135",
+	"res_136",
+	"res_137",
+	"res_138",
+	"res_139",
+	"res_140",
+	"res_141",
+	"res_142",
+	"res_143",
+	"res_144",
+	"res_145",
+	"res_146",
+	"res_147",
+	"res_148",
+	"res_149",
+	"res_150",
+	"res_151",
+	"res_152",
+	"res_153",
+	"res_154",
+	"res_155",
+	"res_156",
+	"res_157",
+	"res_158",
+	"res_159",
+	"res_160",
+	"res_161",
+	"res_162",
+	"res_163",
+	"res_164",
+	"res_165",
+	"res_166",
+	"res_167",
+	"res_168",
+	"res_169",
+	"res_170",
+	"res_171",
+	"res_172",
+	"res_173",
+	"res_174",
+	"res_175",
+	"res_176",
+	"res_177",
+	"res_178",
+	"res_179",
+	"res_180",
+	"res_181",
+	"res_182",
+	"res_183",
+	"res_184",
+	"res_185",
+	"res_186",
+	"res_187",
+	"res_188",
+	"res_189",
+	"res_190",
+	"res_191",
+	"res_192",
+	"res_193",
+	"res_194",
+	"res_195",
+	"res_196",
+	"res_197",
+	"res_198",
+	"res_199",
+	"res_200",
+	"res_201",
+	"res_202",
+	"res_203",
+	"res_204",
+	"res_205",
+	"res_206",
+	"res_207",
+	"res_208",
+	"res_209",
+	"res_210",
+	"res_211",
+	"res_212",
+	"res_213",
+	"res_214",
+	"res_215",
+	"res_216",
+	"res_217",
+	"res_218",
+	"res_219",
+	"res_220",
+	"res_221",
+	"res_222",
+	"res_223",
+	"res_224",
+	"res_225",
+	"res_226",
+	"res_227",
+	"res_228",
+	"res_229",
+	"res_230",
+	"res_231",
+	"res_232",
+	"res_233",
+	"res_234",
+	"res_235",
+	"res_236",
+	"res_237",
+	"res_238",
+	"res_239",
+	"res_240",
+	"res_241",
+	"res_242",
+	"res_243",
+	"res_244",
+	"res_245",
+	"res_246",
+	"res_247",
+	"res_248",
+	"res_249",
+	"res_250",
+	"res_251",
+	"res_252",
+	"res_253",
+	"res_254",
+	"oem_unit",
+};
+
 static inline int
 __scmi_tlm_generic_open(struct inode *ino, struct file *filp,
 			int (*bulk_op)(const struct scmi_tlm_setup *tsp,
@@ -753,6 +1276,10 @@ static int scmi_telemetry_des_initialize(struct device *dev,
 	struct scmi_tlm_inode *des_top_inode;
 
 	des_top_inode = TLM_INODE_SETUP(ti, tsp, &des_dir_cls, NULL, NULL);
+	ti->des_nodes = devm_kcalloc(dev, ti->info->base.num_des,
+				     sizeof(*ti->des_nodes), GFP_KERNEL);
+	if (!ti->des_nodes)
+		return -ENOMEM;
 
 	for (int i = 0; i < ti->info->base.num_des; i++) {
 		const struct scmi_telemetry_de *de = ti->info->des[i];
@@ -796,6 +1323,8 @@ static int scmi_telemetry_des_initialize(struct device *dev,
 				&dei->persistent);
 
 		TLM_INODE_SETUP(ti, tsp, &value_tlmo, de_dir_inode, de);
+
+		ti->des_nodes[i] = de_dir_inode;
 	}
 
 	dev_info(dev, "Found %d Telemetry DE resources.\n", ti->info->base.num_des);
@@ -1633,10 +2162,204 @@ scmi_tlm_node_add(struct super_block *sb, struct dentry *parent,
 	return dentry;
 }
 
+static struct inode *scmi_telemetry_make_dir_inode(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = get_next_ino();
+	inode_init_owner(&nop_mnt_idmap, inode, NULL, S_IFDIR | 0700);
+	simple_inode_init_ts(inode);
+
+	inode->i_op = &tlm_dir_inode_ops;
+	inode->i_fop = &simple_dir_operations;
+
+	return inode;
+}
+
+static struct dentry *scmi_telemetry_topology_dir_alloc(struct super_block *sb,
+							struct dentry *parent,
+							const char *name)
+{
+	struct dentry *dentry;
+	struct inode *inode;
+
+	inode = scmi_telemetry_make_dir_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	dentry = d_alloc_name(parent, name);
+	if (!dentry)
+		return ERR_PTR(-ENOMEM);
+
+	d_add(dentry, inode);
+
+	dget(dentry);
+
+	return dentry;
+}
+
+static struct scmi_tlm_inode *
+scmi_telemetry_de_subdir_clone(struct super_block *sb,
+			       struct scmi_tlm_inode *tlmi,
+			       struct dentry *parent)
+{
+	struct device *dev = tlmi->tsp->dev;
+	struct scmi_tlm_inode *dtlmi;
+	struct dentry *twin, *child;
+	struct qstr qstr;
+
+	/*
+	 * NOTE THAT I CANNOT HARD-LINK A DIRECTORY ... BREAKS VFS
+	 * ...so we just duplicate the tlm_inode here
+	 */
+	dtlmi = devm_kmemdup(dev, tlmi, sizeof(*tlmi), GFP_KERNEL);
+	if (!dtlmi)
+		return ERR_PTR(-ENOMEM);
+
+	qstr.name = dtlmi->cls->name;
+	qstr.len = strlen(dtlmi->cls->name);
+	qstr.hash = full_name_hash(parent, qstr.name, qstr.len);
+
+	/* by_compo_type/<COMPO_TYPE_STR>/<N/<DE_UNIT_TYPE_STR>/<M>/<DE_ID> */
+	twin = d_lookup(parent, &qstr);
+	if (twin) {
+		dev_err(dev, "Found duplicated entry '%s' in topology.\n",
+			dtlmi->cls->name);
+		return ERR_PTR(-EBUSY);
+	}
+
+	twin = d_alloc_name(parent, dtlmi->cls->name);
+	if (!twin)
+		return ERR_PTR(-ENOMEM);
+
+	dtlmi->dentry = twin;
+	dtlmi->parent = NULL;
+	if (!scmi_tlm_inode_initialize(sb, S_IFDIR | dtlmi->cls->mode, dtlmi))
+		return ERR_PTR(-ENOMEM);
+
+	/* Add another dentry to the duplicated inode under another parent */
+	d_add(twin, &dtlmi->vfs_inode);
+
+	/* Hard-link all child of tlmi to the duplicate */
+	spin_lock(&tlmi->dentry->d_parent->d_lock);
+	hlist_for_each_entry(child, &tlmi->dentry->d_children, d_sib) {
+		struct dentry *hdl;
+
+		if (!child->d_inode)
+			continue;
+
+		hdl = d_alloc_name(twin, child->d_name.name);
+		if (!hdl) {
+			spin_unlock(&tlmi->dentry->d_parent->d_lock);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		inode_inc_link_count(child->d_inode);
+		d_add(hdl, child->d_inode);
+	}
+	spin_unlock(&tlmi->dentry->d_parent->d_lock);
+
+	return 0;
+}
+
+static struct dentry *
+scmi_telemetry_topology_path_get(struct super_block *sb, struct dentry *parent,
+				 const char *dname)
+{
+	struct dentry *dentry;
+	struct qstr qstr;
+
+	qstr.name = dname;
+	qstr.len = strlen(dname);
+	qstr.hash = full_name_hash(parent, qstr.name, qstr.len);
+
+	dentry = d_lookup(parent, &qstr);
+	if (!dentry) {
+		dentry = scmi_telemetry_topology_dir_alloc(sb, parent, dname);
+		if (!dentry)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	return dentry;
+}
+
+static int scmi_telemetry_topology_add_node(struct super_block *sb,
+					    struct dentry *top_dentry,
+					    struct scmi_tlm_inode *tlmi)
+{
+	struct dentry *ctype, *cinst, *cunit, *dinst;
+	const struct scmi_telemetry_de *de = tlmi->priv;
+	struct scmi_tlm_de_info *dei = de->info;
+	struct scmi_tlm_inode *dtlmi;
+	char inst_str[32];
+
+	/* by_compo_type/<COMPO_TYPE_STR>/ */
+	ctype = scmi_telemetry_topology_path_get(sb, top_dentry,
+						 compo_types[dei->compo_type]);
+	if (!ctype)
+		return -ENOMEM;
+
+	/* by_compo_type/<COMPO_TYPE_STR>/<N>/ */
+	snprintf(inst_str, 32, "%u", dei->compo_instance_id);
+	cinst = scmi_telemetry_topology_path_get(sb, ctype, inst_str);
+	dput(ctype);
+	if (!cinst)
+		return -ENOMEM;
+
+	/* by_compo_type/<COMPO_TYPE_STR>/<N>/<DE_UNIT_TYPE_STR>/ */
+	cunit = scmi_telemetry_topology_path_get(sb, cinst,
+						 unit_types[dei->unit]);
+	dput(cinst);
+	if (!cunit)
+		return -ENOMEM;
+
+	/* by_compo_type/<COMPO_TYPE_STR>/<N>/<DE_UNIT_TYPE_STR>/<N> */
+	snprintf(inst_str, 32, "%u", dei->instance_id);
+	dinst = scmi_telemetry_topology_path_get(sb, cunit, inst_str);
+	dput(cunit);
+	if (!dinst)
+		return -ENOMEM;
+
+	dtlmi = scmi_telemetry_de_subdir_clone(sb, tlmi, dinst);
+	dput(dinst);
+
+	return PTR_ERR(dtlmi);
+}
+
+static int scmi_telemetry_topology_view_add(struct super_block *sb,
+					    struct scmi_tlm_instance *ti)
+{
+	struct device *dev = ti->tsp->dev;
+	struct dentry *topo;
+
+	topo = scmi_telemetry_topology_dir_alloc(sb, ti->top_inode->dentry,
+						 "components");
+	if (!topo)
+		return -ENOMEM;
+
+	for (int i = 0; i < ti->info->base.num_des; i++) {
+		int ret;
+
+		ret = scmi_telemetry_topology_add_node(sb, topo,
+						       ti->des_nodes[i]);
+		if (ret)
+			dev_err(dev, "Fail to add node %s to topology. Skip.\n",
+				ti->des_nodes[i]->cls->name);
+	}
+
+	dput(topo);
+
+	return 0;
+}
+
 static int scmi_telemetry_instance_register(struct super_block *sb,
 					    struct scmi_tlm_instance *ti)
 {
 	struct dentry *top;
+	int ret;
 
 	/* AT first create instance top dir ... */
 	top = scmi_tlm_node_add(sb, sb->s_root, ti->top_cls.name,
@@ -1664,6 +2387,13 @@ static int scmi_telemetry_instance_register(struct super_block *sb,
 			return PTR_ERR(dentry);
 	}
 
+	/* Add an alternative topological view for the DES nodes */
+	ret = scmi_telemetry_topology_view_add(sb, ti);
+	if (ret)
+		dev_warn(ti->tsp->dev,
+			 "Failed to create topology view for instance %s.\n",
+			 ti->top_cls.name);
+
 	return 0;
 }
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 09/10] include: trace: Add Telemetry trace events
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (7 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 08/10] firmware: arm_scmi: Add Telemetry components view Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-25 20:35 ` [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces Cristian Marussi
  9 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Add custom traces to report Telemetry failed accesses and to report when DE
values are updated internally after a notification is processed.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 include/trace/events/scmi.h | 48 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h
index 703b7bb68e44..b70b26e467b8 100644
--- a/include/trace/events/scmi.h
+++ b/include/trace/events/scmi.h
@@ -7,7 +7,8 @@
 
 #include <linux/tracepoint.h>
 
-#define TRACE_SCMI_MAX_TAG_LEN	6
+#define TRACE_SCMI_MAX_TAG_LEN		6
+#define TRACE_SCMI_TLM_MAX_TAG_LEN	16
 
 TRACE_EVENT(scmi_fc_call,
 	TP_PROTO(u8 protocol_id, u8 msg_id, u32 res_id, u32 val1, u32 val2),
@@ -180,6 +181,51 @@ TRACE_EVENT(scmi_msg_dump,
 		  __entry->tag, __entry->msg_id, __entry->seq, __entry->status,
 		__print_hex_str(__get_dynamic_array(cmd), __entry->len))
 );
+
+TRACE_EVENT(scmi_tlm_access,
+	TP_PROTO(u64 de_id, unsigned char *tag, u64 startm, u64 endm),
+	TP_ARGS(de_id, tag, startm, endm),
+
+	TP_STRUCT__entry(
+		__field(u64, de_id)
+		__array(char, tag, TRACE_SCMI_TLM_MAX_TAG_LEN)
+		__field(u64, startm)
+		__field(u64, endm)
+	),
+
+	TP_fast_assign(
+		__entry->de_id = de_id;
+		strscpy(__entry->tag, tag, TRACE_SCMI_TLM_MAX_TAG_LEN);
+		__entry->startm = startm;
+		__entry->endm = endm;
+	),
+
+	TP_printk("de_id=0x%llX [%s] - startm=%016llX endm=%016llX",
+		  __entry->de_id, __entry->tag, __entry->startm, __entry->endm)
+);
+
+TRACE_EVENT(scmi_tlm_collect,
+	TP_PROTO(u64 ts, u64 de_id, u64 value, unsigned char *tag),
+	TP_ARGS(ts, de_id, value, tag),
+
+	TP_STRUCT__entry(
+		__field(u64, ts)
+		__field(u64, de_id)
+		__field(u64, value)
+		__array(char, tag, TRACE_SCMI_TLM_MAX_TAG_LEN)
+	),
+
+	TP_fast_assign(
+		__entry->ts = ts;
+		__entry->de_id = de_id;
+		__entry->value = value;
+		strscpy(__entry->tag, tag, TRACE_SCMI_TLM_MAX_TAG_LEN);
+	),
+
+	TP_printk("ts=%llu  de_id=0x%04llX  value=%016llu [%s]",
+		  __entry->ts, __entry->de_id, __entry->value, __entry->tag)
+);
+
 #endif /* _TRACE_SCMI_H */
 
 /* This part must be outside protection */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces
  2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
                   ` (8 preceding siblings ...)
  2025-09-25 20:35 ` [PATCH 09/10] include: trace: Add Telemetry trace events Cristian Marussi
@ 2025-09-25 20:35 ` Cristian Marussi
  2025-09-26 13:05   ` kernel test robot
  2025-09-26 19:54   ` kernel test robot
  9 siblings, 2 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-09-25 20:35 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, arm-scmi
  Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi

Track failed SHMTI accesses and received notifications.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/telemetry.c | 57 ++++++++++++++++++++++-----
 1 file changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
index f03000c173c2..aa706518e5b6 100644
--- a/drivers/firmware/arm_scmi/telemetry.c
+++ b/drivers/firmware/arm_scmi/telemetry.c
@@ -23,6 +23,8 @@
 #include "protocols.h"
 #include "notify.h"
 
+#include <trace/events/scmi.h>
+
 /* Updated only after ALL the mandatory features for that version are merged */
 #define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
 
@@ -1021,8 +1023,10 @@ static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
 
 	/* Check for spec compliance */
 	if (USE_LINE_TS(payld) || USE_BLK_TS(payld) ||
-	    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0))
+	    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0)) {
+		trace_scmi_tlm_access(0, "BLK_TS_INVALID", 0, 0);
 		return;
+	}
 
 	/* A BLK_TS descriptor MUST be returned: it is found or it is crated */
 	bts = scmi_telemetry_blkts_lookup(ti->dev, &ti->xa_bts, payld);
@@ -1031,6 +1035,9 @@ static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
 
 	/* Update the descriptor with the lastest TS*/
 	scmi_telemetry_blkts_update(shmti->last_magic, bts);
+
+	trace_scmi_tlm_collect(bts->last_ts, (u64)payld,
+			       bts->last_magic, "SHMTI_BLK_TS");
 }
 
 static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
@@ -1046,8 +1053,10 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
 
 	id = PAYLD_ID(payld);
 	de = xa_load(&ti->xa_des, id);
-	if (!de)
+	if (!de) {
+		trace_scmi_tlm_access(id, "DE_INVALID", 0, 0);
 		return;
+	}
 
 	tde = to_tde(de);
 	/* Update DE location refs if requested: normally done only on enable */
@@ -1094,6 +1103,8 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
 		tde->last_ts = tstamp;
 	else
 		tde->last_ts = 0;
+
+	trace_scmi_tlm_collect(0, de->info->id, tde->last_val, "SHMTI_DE_UPDT");
 }
 
 static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
@@ -1139,8 +1150,10 @@ static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
 		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);
 
 		startm = TDCF_START_SEQ_GET(tdcf);
-		if (IS_BAD_START_SEQ(startm))
+		if (IS_BAD_START_SEQ(startm)) {
+			trace_scmi_tlm_access(0, "MSEQ_BADSTART", startm, 0);
 			continue;
+		}
 
 		/* On a BAD_SEQ this will be updated on the next attempt */
 		shmti->last_magic = startm;
@@ -1152,18 +1165,25 @@ static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
 
 			used_qwords = scmi_telemetry_tdcf_line_parse(ti, next,
 								     shmti, update);
-			if (qwords < used_qwords)
+			if (qwords < used_qwords) {
+				trace_scmi_tlm_access(PAYLD_ID(next),
+						      "BAD_QWORDS", startm, 0);
 				return -EINVAL;
+			}
 
 			next += used_qwords * 8;
 			qwords -= used_qwords;
 		}
 
 		endm = TDCF_END_SEQ_GET(eplg);
+		if (startm != endm)
+			trace_scmi_tlm_access(0, "MSEQ_MISMATCH", startm, endm);
 	} while (startm != endm && --retries);
 
-	if (startm != endm)
+	if (startm != endm) {
+		trace_scmi_tlm_access(0, "TDCF_SCAN_FAIL", startm, endm);
 		return -EPROTO;
+	}
 
 	return 0;
 }
@@ -1544,6 +1564,8 @@ static void scmi_telemetry_scan_update(struct telemetry_info *ti, u64 ts)
 			tde->last_ts = tstamp;
 		else
 			tde->last_ts = 0;
+
+		trace_scmi_tlm_collect(ts, de->info->id, tde->last_val, "FC_UPDATE");
 	}
 }
 
@@ -1622,8 +1644,11 @@ static int scmi_telemetry_tdcf_de_parse(struct telemetry_de *tde,
 		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);
 
 		startm = TDCF_START_SEQ_GET(tdcf);
-		if (IS_BAD_START_SEQ(startm))
+		if (IS_BAD_START_SEQ(startm)) {
+			trace_scmi_tlm_access(tde->de.info->id, "MSEQ_BADSTART",
+					      startm, 0);
 			continue;
+		}
 
 		/* Has anything changed at all at the SHMTI level ? */
 		scoped_guard(mutex, &tde->mtx) {
@@ -1639,11 +1664,16 @@ static int scmi_telemetry_tdcf_de_parse(struct telemetry_de *tde,
 		if (DATA_INVALID(payld))
 			return -EINVAL;
 
-		if (IS_BLK_TS(payld))
+		if (IS_BLK_TS(payld)) {
+			trace_scmi_tlm_access(tde->de.info->id,
+					      "BAD_DE_META", 0, 0);
 			return -EINVAL;
+		}
 
-		if (le32_to_cpu(payld->id) != tde->de.info->id)
+		if (le32_to_cpu(payld->id) != tde->de.info->id) {
+			trace_scmi_tlm_access(tde->de.info->id, "DE_INVALID", 0, 0);
 			return -EINVAL;
+		}
 
 		/* Data is always valid since NOT handling BLK TS lines here */
 		*val = LINE_DATA_GET(&payld->l);
@@ -1667,10 +1697,16 @@ static int scmi_telemetry_tdcf_de_parse(struct telemetry_de *tde,
 		}
 
 		endm = TDCF_END_SEQ_GET(tde->eplg);
+		if (startm != endm)
+			trace_scmi_tlm_access(tde->de.info->id, "MSEQ_MISMATCH",
+					      startm, endm);
 	} while (startm != endm && --retries);
 
-	if (startm != endm)
+	if (startm != endm) {
+		trace_scmi_tlm_access(tde->de.info->id, "TDCF_DE_FAIL",
+				      startm, endm);
 		return -EPROTO;
+	}
 
 	guard(mutex)(&tde->mtx);
 	tde->last_magic = startm;
@@ -1840,6 +1876,9 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti,
 			tde->last_ts = LINE_TSTAMP_GET(&payld->tsl);
 		else
 			tde->last_ts = 0;
+
+		trace_scmi_tlm_collect(timestamp, tde->de.info->id, tde->last_val,
+				       "MESSAGE");
 	}
 }
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces
  2025-09-25 20:35 ` [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces Cristian Marussi
@ 2025-09-26 13:05   ` kernel test robot
  2025-09-26 19:54   ` kernel test robot
  1 sibling, 0 replies; 32+ messages in thread
From: kernel test robot @ 2025-09-26 13:05 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi
  Cc: oe-kbuild-all, sudeep.holla, james.quinlan, f.fainelli,
	vincent.guittot, etienne.carriere, peng.fan, michal.simek,
	quic_sibis, dan.carpenter, d-gole, souvik.chakravarty,
	Cristian Marussi

Hi Cristian,

kernel test robot noticed the following build warnings:

[auto build test WARNING on soc/for-next]
[also build test WARNING on trace/for-next linus/master v6.17-rc7 next-20250925]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cristian-Marussi/firmware-arm_scmi-Define-a-common-SCMI_MAX_PROTOCOLS-value/20250926-044350
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/20250925203554.482371-11-cristian.marussi%40arm.com
patch subject: [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces
config: nios2-randconfig-001-20250926 (https://download.01.org/0day-ci/archive/20250926/202509262034.xwphcmtO-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250926/202509262034.xwphcmtO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509262034.xwphcmtO-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/firmware/arm_scmi/telemetry.c: In function 'scmi_telemetry_tdcf_blkts_parse':
>> drivers/firmware/arm_scmi/telemetry.c:1039:46: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
    1039 |         trace_scmi_tlm_collect(bts->last_ts, (u64)payld,
         |                                              ^


vim +1039 drivers/firmware/arm_scmi/telemetry.c

  1017	
  1018	static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
  1019						    struct payload __iomem *payld,
  1020						    struct telemetry_shmti *shmti)
  1021	{
  1022		struct telemetry_block_ts *bts;
  1023	
  1024		/* Check for spec compliance */
  1025		if (USE_LINE_TS(payld) || USE_BLK_TS(payld) ||
  1026		    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0)) {
  1027			trace_scmi_tlm_access(0, "BLK_TS_INVALID", 0, 0);
  1028			return;
  1029		}
  1030	
  1031		/* A BLK_TS descriptor MUST be returned: it is found or it is crated */
  1032		bts = scmi_telemetry_blkts_lookup(ti->dev, &ti->xa_bts, payld);
  1033		if (WARN_ON(!bts))
  1034			return;
  1035	
  1036		/* Update the descriptor with the lastest TS*/
  1037		scmi_telemetry_blkts_update(shmti->last_magic, bts);
  1038	
> 1039		trace_scmi_tlm_collect(bts->last_ts, (u64)payld,
  1040				       bts->last_magic, "SHMTI_BLK_TS");
  1041	}
  1042	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 04/10] uapi: Add ARM SCMI definitions
  2025-09-25 20:35 ` [PATCH 04/10] uapi: Add ARM SCMI definitions Cristian Marussi
@ 2025-09-26 14:30   ` kernel test robot
  2025-10-17 15:14   ` Jonathan Cameron
  1 sibling, 0 replies; 32+ messages in thread
From: kernel test robot @ 2025-09-26 14:30 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi
  Cc: oe-kbuild-all, sudeep.holla, james.quinlan, f.fainelli,
	vincent.guittot, etienne.carriere, peng.fan, michal.simek,
	quic_sibis, dan.carpenter, d-gole, souvik.chakravarty,
	Cristian Marussi

Hi Cristian,

kernel test robot noticed the following build errors:

[auto build test ERROR on soc/for-next]
[also build test ERROR on trace/for-next linus/master v6.17-rc7 next-20250925]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cristian-Marussi/firmware-arm_scmi-Define-a-common-SCMI_MAX_PROTOCOLS-value/20250926-044350
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/20250925203554.482371-5-cristian.marussi%40arm.com
patch subject: [PATCH 04/10] uapi: Add ARM SCMI definitions
config: x86_64-buildonly-randconfig-005-20250926 (https://download.01.org/0day-ci/archive/20250926/202509262227.VZVSICFi-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250926/202509262227.VZVSICFi-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509262227.VZVSICFi-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from <command-line>:
>> ./usr/include/linux/scmi.h:43:1: error: C++ style comments are not allowed in ISO C90
      43 | ///TODO more flags
         | ^
   ./usr/include/linux/scmi.h:43:1: note: (this will be reported only once per input file)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
@ 2025-09-26 17:27   ` kernel test robot
  2025-10-17 15:37   ` Jonathan Cameron
  2025-10-28 11:43   ` Lukasz Luba
  2 siblings, 0 replies; 32+ messages in thread
From: kernel test robot @ 2025-09-26 17:27 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi
  Cc: oe-kbuild-all, sudeep.holla, james.quinlan, f.fainelli,
	vincent.guittot, etienne.carriere, peng.fan, michal.simek,
	quic_sibis, dan.carpenter, d-gole, souvik.chakravarty,
	Cristian Marussi

Hi Cristian,

kernel test robot noticed the following build warnings:

[auto build test WARNING on soc/for-next]
[also build test WARNING on trace/for-next linus/master v6.17-rc7 next-20250926]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cristian-Marussi/firmware-arm_scmi-Define-a-common-SCMI_MAX_PROTOCOLS-value/20250926-044350
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/20250925203554.482371-6-cristian.marussi%40arm.com
patch subject: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
config: m68k-randconfig-r131-20250926 (https://download.01.org/0day-ci/archive/20250927/202509270119.Kb7lc7u3-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250927/202509270119.Kb7lc7u3-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509270119.Kb7lc7u3-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/firmware/arm_scmi/telemetry.c:378:48: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:379:53: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:380:53: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:381:42: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:382:39: sparse: sparse: restricted __le32 degrades to integer
>> drivers/firmware/arm_scmi/telemetry.c:480:36: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:480:36: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:480:36: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:484:36: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:485:34: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:486:30: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/telemetry.c:487:32: sparse: sparse: restricted __le32 degrades to integer
>> drivers/firmware/arm_scmi/telemetry.c:672:20: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] flags @@     got unsigned long @@
   drivers/firmware/arm_scmi/telemetry.c:672:20: sparse:     expected restricted __le32 [usertype] flags
   drivers/firmware/arm_scmi/telemetry.c:672:20: sparse:     got unsigned long
   drivers/firmware/arm_scmi/telemetry.c:699:36: sparse: sparse: restricted __le32 degrades to integer
>> drivers/firmware/arm_scmi/telemetry.c:724:56: sparse: sparse: incorrect type in assignment (different base types) @@     expected unsigned int @@     got restricted __le32 const @@
   drivers/firmware/arm_scmi/telemetry.c:724:56: sparse:     expected unsigned int
   drivers/firmware/arm_scmi/telemetry.c:724:56: sparse:     got restricted __le32 const
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: cast to restricted __le32
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:955:18: sparse: sparse: cast removes address space '__iomem' of expression
>> drivers/firmware/arm_scmi/telemetry.c:984:28: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected struct payload [noderef] __iomem *payld @@     got struct payload *payld @@
   drivers/firmware/arm_scmi/telemetry.c:984:28: sparse:     expected struct payload [noderef] __iomem *payld
   drivers/firmware/arm_scmi/telemetry.c:984:28: sparse:     got struct payload *payld
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1024:37: sparse: sparse: cast to restricted __le32
>> drivers/firmware/arm_scmi/telemetry.c:1028:65: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected struct payload *payld @@     got struct payload [noderef] __iomem *payld @@
   drivers/firmware/arm_scmi/telemetry.c:1028:65: sparse:     expected struct payload *payld
   drivers/firmware/arm_scmi/telemetry.c:1028:65: sparse:     got struct payload [noderef] __iomem *payld
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1047:14: sparse: sparse: cast to restricted __le32
>> drivers/firmware/arm_scmi/telemetry.c:1056:29: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void *_eplg @@     got void [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1056:29: sparse:     expected void *_eplg
   drivers/firmware/arm_scmi/telemetry.c:1056:29: sparse:     got void [noderef] __iomem *
>> drivers/firmware/arm_scmi/telemetry.c:1056:27: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void [noderef] __iomem *eplg @@     got void *_eplg @@
   drivers/firmware/arm_scmi/telemetry.c:1056:27: sparse:     expected void [noderef] __iomem *eplg
   drivers/firmware/arm_scmi/telemetry.c:1056:27: sparse:     got void *_eplg
   drivers/firmware/arm_scmi/telemetry.c:1057:32: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1057:48: sparse: sparse: cast removes address space '__iomem' of expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1079:77: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected struct payload *payld @@     got struct payload [noderef] __iomem *payld @@
   drivers/firmware/arm_scmi/telemetry.c:1079:77: sparse:     expected struct payload *payld
   drivers/firmware/arm_scmi/telemetry.c:1079:77: sparse:     got struct payload [noderef] __iomem *payld
   drivers/firmware/arm_scmi/telemetry.c:1129:22: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void *_eplg @@     got void [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1129:22: sparse:     expected void *_eplg
   drivers/firmware/arm_scmi/telemetry.c:1129:22: sparse:     got void [noderef] __iomem *
>> drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected struct prlg *_p @@     got struct prlg [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse:     expected struct prlg *_p
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse:     got struct prlg [noderef] __iomem *
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1141:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1162:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1219:37: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void *_eplg @@     got void [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1219:37: sparse:     expected void *_eplg
   drivers/firmware/arm_scmi/telemetry.c:1219:37: sparse:     got void [noderef] __iomem *
   drivers/firmware/arm_scmi/telemetry.c:1219:35: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void [noderef] __iomem *eplg @@     got void *_eplg @@
   drivers/firmware/arm_scmi/telemetry.c:1219:35: sparse:     expected void [noderef] __iomem *eplg
   drivers/firmware/arm_scmi/telemetry.c:1219:35: sparse:     got void *_eplg
>> drivers/firmware/arm_scmi/telemetry.c:1221:31: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected struct payload *payld @@     got void [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1221:31: sparse:     expected struct payload *payld
   drivers/firmware/arm_scmi/telemetry.c:1221:31: sparse:     got void [noderef] __iomem *
>> drivers/firmware/arm_scmi/telemetry.c:1305:20: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1305:20: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1305:20: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1312:36: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1312:36: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1312:36: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1315:36: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1315:36: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1315:36: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1409:20: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] flags @@     got unsigned long @@
   drivers/firmware/arm_scmi/telemetry.c:1409:20: sparse:     expected restricted __le32 [usertype] flags
   drivers/firmware/arm_scmi/telemetry.c:1409:20: sparse:     got unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1411:28: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1411:28: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1411:28: sparse:    right side has type unsigned long
>> drivers/firmware/arm_scmi/telemetry.c:1471:26: sparse: sparse: incorrect type in assignment (different base types) @@     expected unsigned int [usertype] interval @@     got restricted __le32 [usertype] @@
   drivers/firmware/arm_scmi/telemetry.c:1471:26: sparse:     expected unsigned int [usertype] interval
   drivers/firmware/arm_scmi/telemetry.c:1471:26: sparse:     got restricted __le32 [usertype]
>> drivers/firmware/arm_scmi/telemetry.c:1479:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] grp_id @@     got unsigned int res_id @@
   drivers/firmware/arm_scmi/telemetry.c:1479:21: sparse:     expected restricted __le32 [usertype] grp_id
   drivers/firmware/arm_scmi/telemetry.c:1479:21: sparse:     got unsigned int res_id
>> drivers/firmware/arm_scmi/telemetry.c:1480:22: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] control @@     got unsigned long @@
   drivers/firmware/arm_scmi/telemetry.c:1480:22: sparse:     expected restricted __le32 [usertype] control
   drivers/firmware/arm_scmi/telemetry.c:1480:22: sparse:     got unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1481:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1481:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1481:22: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1483:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1483:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1483:22: sparse:    right side has type unsigned long
>> drivers/firmware/arm_scmi/telemetry.c:1484:28: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] sampling_rate @@     got unsigned int [assigned] [usertype] interval @@
   drivers/firmware/arm_scmi/telemetry.c:1484:28: sparse:     expected restricted __le32 [usertype] sampling_rate
   drivers/firmware/arm_scmi/telemetry.c:1484:28: sparse:     got unsigned int [assigned] [usertype] interval
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected struct prlg *_p @@     got struct prlg [noderef] __iomem * @@
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse:     expected struct prlg *_p
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse:     got struct prlg [noderef] __iomem *
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1624:26: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected struct eplg *_e @@     got void [noderef] __iomem *eplg @@
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse:     expected struct eplg *_e
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse:     got void [noderef] __iomem *eplg
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1669:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1825:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1837:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1840:40: sparse: sparse: cast to restricted __le32
>> drivers/firmware/arm_scmi/telemetry.c:1869:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] grp_id @@     got int grp_id @@
   drivers/firmware/arm_scmi/telemetry.c:1869:21: sparse:     expected restricted __le32 [usertype] grp_id
   drivers/firmware/arm_scmi/telemetry.c:1869:21: sparse:     got int grp_id
   drivers/firmware/arm_scmi/telemetry.c:1870:22: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] control @@     got unsigned long @@
   drivers/firmware/arm_scmi/telemetry.c:1870:22: sparse:     expected restricted __le32 [usertype] control
   drivers/firmware/arm_scmi/telemetry.c:1870:22: sparse:     got unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1871:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1871:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1871:22: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1873:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1873:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:22: sparse:    right side has type unsigned long
>> drivers/firmware/arm_scmi/telemetry.c:1882:63: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned int num_dwords @@     got restricted __le32 [usertype] num_dwords @@
   drivers/firmware/arm_scmi/telemetry.c:1882:63: sparse:     expected unsigned int num_dwords
   drivers/firmware/arm_scmi/telemetry.c:1882:63: sparse:     got restricted __le32 [usertype] num_dwords
>> drivers/firmware/arm_scmi/telemetry.c:1883:58: sparse: sparse: incorrect type in argument 3 (different base types) @@     expected unsigned int *dwords @@     got restricted __le32 * @@
   drivers/firmware/arm_scmi/telemetry.c:1883:58: sparse:     expected unsigned int *dwords
   drivers/firmware/arm_scmi/telemetry.c:1883:58: sparse:     got restricted __le32 *
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:905:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:917:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1023:13: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1023:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1024:13: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1041:25: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1066:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1069:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1070:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1071:28: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1106:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1106:46: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1110:13: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1113:14: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1148:26: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1504:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1507:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1639:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1642:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1645:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1649:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1652:30: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1654:36: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1655:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1656:36: sparse: sparse: dereference of noderef expression

vim +378 drivers/firmware/arm_scmi/telemetry.c

   354	
   355	static int
   356	scmi_telemetry_protocol_attributes_get(const struct scmi_protocol_handle *ph,
   357					       struct telemetry_info *ti)
   358	{
   359		int ret;
   360		struct scmi_xfer *t;
   361		struct scmi_msg_resp_telemetry_protocol_attributes *resp;
   362	
   363		ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
   364					      0, sizeof(*resp), &t);
   365		if (ret)
   366			return ret;
   367	
   368		resp = t->rx.buf;
   369		ret = ph->xops->do_xfer(ph, t);
   370		if (!ret) {
   371			__le32 attr = resp->attributes;
   372	
   373			ti->info.base.num_des = le32_to_cpu(resp->de_num);
   374			ti->info.base.num_groups = le32_to_cpu(resp->groups_num);
   375			for (int i = 0; i < SCMI_TLM_DE_IMPL_MAX_DWORDS; i++)
   376				ti->info.base.de_impl_version[i] =
   377					le32_to_cpu(resp->de_implementation_rev_dword[i]);
 > 378			ti->info.single_read_support = SUPPORTS_SINGLE_READ(attr);
   379			ti->info.continuos_update_support = SUPPORTS_CONTINUOS_UPDATE(attr);
   380			ti->info.per_group_config_support = SUPPORTS_PER_GROUP_CONFIG(attr);
   381			ti->info.reset_support = SUPPORTS_RESET(attr);
   382			ti->info.fc_support = SUPPORTS_FC(attr);
   383			ti->num_shmti = le32_get_bits(attr, GENMASK(15, 0));
   384			/* Allocate DEs descriptors */
   385			ti->info.des = devm_kcalloc(ph->dev, ti->info.base.num_des,
   386						    sizeof(*ti->info.des), GFP_KERNEL);
   387			if (!ti->info.des) {
   388				ret = -ENOMEM;
   389				goto out;
   390			}
   391	
   392			/* Allocate a set of contiguous DE info descriptors. */
   393			ti->info.des_store = devm_kcalloc(ph->dev, ti->info.base.num_des,
   394							  sizeof(*ti->info.des_store),
   395							  GFP_KERNEL);
   396			if (!ti->info.des_store) {
   397				ret = -ENOMEM;
   398				goto out;
   399			}
   400	
   401			/* Allocate DE GROUPS descriptors */
   402			ti->info.groups = devm_kcalloc(ph->dev, ti->info.base.num_groups,
   403						       sizeof(*ti->info.groups), GFP_KERNEL);
   404			if (!ti->info.groups) {
   405				ret = -ENOMEM;
   406				goto out;
   407			}
   408	
   409			/* Allocate a set of contiguous Group info descriptors. */
   410			ti->info.grps_store = devm_kcalloc(ph->dev, ti->info.base.num_groups,
   411							   sizeof(*ti->info.grps_store),
   412							   GFP_KERNEL);
   413			if (!ti->info.grps_store) {
   414				ret = -ENOMEM;
   415				goto out;
   416			}
   417	
   418			for (int i = 0; i < ti->info.base.num_groups; i++) {
   419				ti->info.grps_store[i].id = i;
   420				/* Bind contiguous Group info struct */
   421				ti->info.groups[i].info = &ti->info.grps_store[i];
   422			}
   423		}
   424	
   425	out:
   426		ph->xops->xfer_put(ph, t);
   427	
   428		return ret;
   429	}
   430	
   431	static void iter_tlm_prepare_message(void *message,
   432					     unsigned int desc_index, const void *priv)
   433	{
   434		put_unaligned_le32(desc_index, message);
   435	}
   436	
   437	static int iter_de_descr_update_state(struct scmi_iterator_state *st,
   438					      const void *response, void *priv)
   439	{
   440		const struct scmi_msg_resp_telemetry_de_description *r = response;
   441		struct scmi_tlm_de_priv *p = priv;
   442	
   443		st->num_returned = le32_get_bits(r->num_desc, GENMASK(15, 0));
   444		st->num_remaining = le32_get_bits(r->num_desc, GENMASK(31, 16));
   445	
   446		/* Initialized to first descriptor */
   447		p->next = (void *)r->desc;
   448	
   449		return 0;
   450	}
   451	
   452	static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,
   453						  const void *response,
   454						  struct scmi_iterator_state *st,
   455						  void *priv)
   456	{
   457		struct telemetry_de *tde;
   458		struct scmi_tlm_de_priv *p = priv;
   459		const struct scmi_de_desc *desc = p->next;
   460		unsigned int grp_id;
   461		int ret;
   462	
   463		tde = to_tde(p->ti->info.des[st->desc_index + st->loop_idx]);
   464	
   465		tde->de.info->id = le32_to_cpu(desc->id);
   466		grp_id = le32_to_cpu(desc->grp_id);
   467		if (grp_id != SCMI_TLM_GRP_INVALID) {
   468			/* Group descriptors are empty but allocated at this point */
   469			if (grp_id >= p->ti->info.base.num_groups)
   470				return -EINVAL;
   471	
   472			/* Link to parent group */
   473			tde->de.info->grp_id = grp_id;
   474			tde->de.grp = &p->ti->info.groups[grp_id];
   475		}
   476		tde->de.info->data_sz = le32_to_cpu(desc->data_sz);
   477		tde->de.info->type = GET_DE_TYPE(desc);
   478		tde->de.info->unit = GET_DE_UNIT(desc);
   479		tde->de.info->unit_exp = GET_DE_UNIT_EXP(desc);
 > 480		tde->de.info->tstamp_exp = GET_DE_TSTAMP_EXP(desc);
   481		tde->de.info->instance_id = GET_DE_INSTA_ID(desc);
   482		tde->de.info->compo_instance_id = GET_COMPO_INSTA_ID(desc);
   483		tde->de.info->compo_type = GET_COMPO_TYPE(desc);
   484		tde->de.info->persistent = IS_PERSISTENT(desc);
   485		tde->de.tstamp_support = IS_TSTAMP_SUPPORTED(desc);
   486		tde->de.fc_support = IS_FC_SUPPORTED(desc);
   487		tde->de.name_support = IS_NAME_SUPPORTED(desc);
   488		p->next += sizeof(*desc);
   489		if (tde->de.fc_support) {
   490			u32 size;
   491			u64 phys_addr;
   492			void __iomem *addr;
   493			struct de_desc_fc *dfc;
   494	
   495			dfc = p->next;
   496			phys_addr = le32_to_cpu(dfc->addr_low);
   497			phys_addr |= (u64)le32_to_cpu(dfc->addr_high) << 32;
   498	
   499			size = le32_to_cpu(dfc->size);
   500			addr = devm_ioremap(ph->dev, phys_addr, size);
   501			if (!addr)
   502				return -EADDRNOTAVAIL;
   503	
   504			tde->base = addr;
   505			tde->offset = 0;
   506			tde->fc_size = size;
   507	
   508			/* Variably sized depending on FC support */
   509			p->next += sizeof(*dfc);
   510		}
   511	
   512		if (tde->de.name_support) {
   513			const char *de_name = p->next;
   514	
   515			strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE);
   516			//tde->de.name = tde->name;
   517	
   518			/* Variably sized depending on name support */
   519			p->next += SCMI_SHORT_NAME_MAX_SIZE;
   520		}
   521	
   522		/* Store DE pointer by de_id */
   523		ret = xa_insert(&p->ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL);
   524		if (ret)
   525			return ret;
   526	
   527		/* Account for this DE in group num_de counter */
   528		if (tde->de.grp)
   529			tde->de.grp->info->num_des++;
   530	
   531		return 0;
   532	}
   533	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces
  2025-09-25 20:35 ` [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces Cristian Marussi
  2025-09-26 13:05   ` kernel test robot
@ 2025-09-26 19:54   ` kernel test robot
  1 sibling, 0 replies; 32+ messages in thread
From: kernel test robot @ 2025-09-26 19:54 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi
  Cc: oe-kbuild-all, sudeep.holla, james.quinlan, f.fainelli,
	vincent.guittot, etienne.carriere, peng.fan, michal.simek,
	quic_sibis, dan.carpenter, d-gole, souvik.chakravarty,
	Cristian Marussi

Hi Cristian,

kernel test robot noticed the following build warnings:

[auto build test WARNING on soc/for-next]
[also build test WARNING on trace/for-next linus/master v6.17-rc7 next-20250926]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Cristian-Marussi/firmware-arm_scmi-Define-a-common-SCMI_MAX_PROTOCOLS-value/20250926-044350
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/20250925203554.482371-11-cristian.marussi%40arm.com
patch subject: [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces
config: m68k-randconfig-r131-20250926 (https://download.01.org/0day-ci/archive/20250927/202509270354.Nlj2JlQL-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250927/202509270354.Nlj2JlQL-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509270354.Nlj2JlQL-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected struct eplg *_e @@     got void [noderef] __iomem *eplg @@
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse:     expected struct eplg *_e
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse:     got void [noderef] __iomem *eplg
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1699:24: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1861:25: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1873:33: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1876:40: sparse: sparse: cast to restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1908:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] grp_id @@     got int grp_id @@
   drivers/firmware/arm_scmi/telemetry.c:1908:21: sparse:     expected restricted __le32 [usertype] grp_id
   drivers/firmware/arm_scmi/telemetry.c:1908:21: sparse:     got int grp_id
   drivers/firmware/arm_scmi/telemetry.c:1909:22: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] control @@     got unsigned long @@
   drivers/firmware/arm_scmi/telemetry.c:1909:22: sparse:     expected restricted __le32 [usertype] control
   drivers/firmware/arm_scmi/telemetry.c:1909:22: sparse:     got unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1910:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1910:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1910:22: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1912:22: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/telemetry.c:1912:22: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/telemetry.c:1912:22: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/telemetry.c:1921:63: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned int num_dwords @@     got restricted __le32 [usertype] num_dwords @@
   drivers/firmware/arm_scmi/telemetry.c:1921:63: sparse:     expected unsigned int num_dwords
   drivers/firmware/arm_scmi/telemetry.c:1921:63: sparse:     got restricted __le32 [usertype] num_dwords
   drivers/firmware/arm_scmi/telemetry.c:1922:58: sparse: sparse: incorrect type in argument 3 (different base types) @@     expected unsigned int *dwords @@     got restricted __le32 * @@
   drivers/firmware/arm_scmi/telemetry.c:1922:58: sparse:     expected unsigned int *dwords
   drivers/firmware/arm_scmi/telemetry.c:1922:58: sparse:     got restricted __le32 *
   drivers/firmware/arm_scmi/telemetry.c:907:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:907:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:907:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:907:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:919:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:919:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:919:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:919:32: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1025:13: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1025:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1026:13: sparse: sparse: dereference of noderef expression
>> drivers/firmware/arm_scmi/telemetry.c:1039:51: sparse: sparse: non size-preserving pointer to integer cast
   drivers/firmware/arm_scmi/telemetry.c:1048:25: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1075:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1075:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1075:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1075:15: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1078:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1079:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1079:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1079:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1079:34: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1080:28: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1117:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1117:46: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1121:13: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1124:14: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1161:26: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1524:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1524:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1524:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1524:16: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1527:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1527:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1527:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1527:35: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1664:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1667:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1673:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1673:21: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1679:24: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1682:30: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1684:36: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1685:43: sparse: sparse: dereference of noderef expression
   drivers/firmware/arm_scmi/telemetry.c:1686:36: sparse: sparse: dereference of noderef expression

vim +1039 drivers/firmware/arm_scmi/telemetry.c

  1017	
  1018	static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
  1019						    struct payload __iomem *payld,
  1020						    struct telemetry_shmti *shmti)
  1021	{
  1022		struct telemetry_block_ts *bts;
  1023	
  1024		/* Check for spec compliance */
  1025		if (USE_LINE_TS(payld) || USE_BLK_TS(payld) ||
  1026		    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0)) {
  1027			trace_scmi_tlm_access(0, "BLK_TS_INVALID", 0, 0);
  1028			return;
  1029		}
  1030	
  1031		/* A BLK_TS descriptor MUST be returned: it is found or it is crated */
  1032		bts = scmi_telemetry_blkts_lookup(ti->dev, &ti->xa_bts, payld);
  1033		if (WARN_ON(!bts))
  1034			return;
  1035	
  1036		/* Update the descriptor with the lastest TS*/
  1037		scmi_telemetry_blkts_update(shmti->last_magic, bts);
  1038	
> 1039		trace_scmi_tlm_collect(bts->last_ts, (u64)payld,
  1040				       bts->last_magic, "SHMTI_BLK_TS");
  1041	}
  1042	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex
  2025-09-25 20:35 ` [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex Cristian Marussi
@ 2025-10-17 15:07   ` Jonathan Cameron
  2025-10-21  9:36     ` Cristian Marussi
  0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-17 15:07 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:46 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Currently the mutex dedicated to the protection of the list of registered
> protocols is held during all the protocol initialization phase.
> 
> Such a wide locking region is not needed and causes problem when trying to
> initialize notifications from within a protocol initialization routine.
> 
> Reduce the scope of the protocol mutex.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

Hi Cristian.  A few things inline and this runs into one of the things
that is dangerous to do with guard() or the other cleanup.h magic
(and documented there!)

> ---
>  drivers/firmware/arm_scmi/driver.c | 53 +++++++++++++++---------------
>  1 file changed, 26 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index bd56a877fdfc..71ee25b78624 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -17,6 +17,7 @@
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>  
>  #include <linux/bitmap.h>
> +#include <linux/cleanup.h>
>  #include <linux/debugfs.h>
>  #include <linux/device.h>
>  #include <linux/export.h>
> @@ -2179,10 +2180,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
>  	if (ret)
>  		goto clean;
>  
> -	ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
> -			GFP_KERNEL);
> -	if (ret != proto->id)
> -		goto clean;
> +	/* Finally register the initialized protocol */
> +	scoped_guard(mutex, &info->protocols_mtx) {

See the guidance in cleanup.h on mixing goto and anything defined in that file.

In some compilers, if you hit the goto above and hence jump over this
the cleanup variable will still be instantiated, but not initialized leading to
a potential attempt to unlock random memory.

Either this needs more substantial rework, or just handling the mutex with
out using guards.


> +		ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
> +				GFP_KERNEL);
> +		if (ret != proto->id)
> +			goto clean;
> +	}
>  
>  	/*
>  	 * Warn but ignore events registration errors since we do not want
> @@ -2243,25 +2247,22 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
>  static struct scmi_protocol_instance * __must_check
>  scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
>  {
> -	struct scmi_protocol_instance *pi;
> +	struct scmi_protocol_instance *pi = ERR_PTR(-EPROBE_DEFER);
>  	struct scmi_info *info = handle_to_scmi_info(handle);
> +	const struct scmi_protocol *proto;
>  
> -	mutex_lock(&info->protocols_mtx);
> -	pi = idr_find(&info->protocols, protocol_id);
> -
> -	if (pi) {
> -		refcount_inc(&pi->users);
> -	} else {
> -		const struct scmi_protocol *proto;
> -
> -		/* Fails if protocol not registered on bus */
> -		proto = scmi_protocol_get(protocol_id, &info->version);
> -		if (proto)
> -			pi = scmi_alloc_init_protocol_instance(info, proto);
> -		else
> -			pi = ERR_PTR(-EPROBE_DEFER);
> +	scoped_guard(mutex, &info->protocols_mtx) {
> +		pi = idr_find(&info->protocols, protocol_id);
> +		if (pi) {

if !pi we carry on with it NULL, which is a behavior change from
before where it would be ERR_PTR(-EPROBE_DEFER);

That might not matter, but it's not 'obviously' a safe change.

> +			refcount_inc(&pi->users);
> +			return pi;
> +		}
>  	}
> -	mutex_unlock(&info->protocols_mtx);
> +
> +	/* Fails if protocol not registered on bus */
> +	proto = scmi_protocol_get(protocol_id, &info->version);
> +	if (proto)
Trivial but I'd flip the logic to
	if (!proto)
		return ERR_PTR(-EPROBE_DEFER);
assuming a NULL return as mentioned above isn't the intent.
Then
	return scmi_alloc_init_protocol_instance(info, proto);

> +		pi = scmi_alloc_init_protocol_instance(info, proto);
>  
>  	return pi;
>  }


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications
  2025-09-25 20:35 ` [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications Cristian Marussi
@ 2025-10-17 15:10   ` Jonathan Cameron
  2025-10-21  9:37     ` Cristian Marussi
  0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-17 15:10 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:47 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Allow protocols themselves to register for their own notifications and
> providing their own notifier callbacks. While at that, allow for a protocol
> to register events with compilation-time unknown report/event sizes: such
> events will use the maximum transport size.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Hi Cristian,

A few drive by comments...

> diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
> index 78e9e27dc9ec..3e623c14745d 100644
> --- a/drivers/firmware/arm_scmi/notify.c
> +++ b/drivers/firmware/arm_scmi/notify.c
> @@ -593,7 +593,12 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
>  	if (!r_evt)
>  		return -EINVAL;
>  
> -	if (len > r_evt->evt->max_payld_sz) {
> +	/* Events with a zero max_payld_sz are sized to be of the maximum
Local multiline comment syntax seems to be
	/*
	 * Events...

> +	 * size allowed by the transport: no need to be size-checked here
> +	 * since the transport layer would have already dropped such
> +	 * over-sized messages.
> +	 */
> +	if (r_evt->evt->max_payld_sz && len > r_evt->evt->max_payld_sz) {

> diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
> index d62c4469d1fd..2e40a7bb5b01 100644
> --- a/drivers/firmware/arm_scmi/protocols.h
> +++ b/drivers/firmware/arm_scmi/protocols.h
> @@ -161,8 +161,13 @@ struct scmi_proto_helpers_ops;
>   * @dev: A reference to the associated SCMI instance device (handle->dev).
>   * @xops: A reference to a struct holding refs to the core xfer operations that
>   *	  can be used by the protocol implementation to generate SCMI messages.
> + * @hops: A reference to a struct holding refs to the common helper operations
> + *	  that can be used by the protocol implementation.

@hops isn't added in this patch so either it should be handled in where it was
added, or if that was missed a precursor patch to this one.

>   * @set_priv: A method to set protocol private data for this instance.
>   * @get_priv: A method to get protocol private data previously set.
> + * @notifier_register: A method to register interest for notifications from
> + *		       within a protocol implementation unit: notifiers can
> + *		       be registered only for the same protocol.
>   *
>   * This structure represents a protocol initialized against specific SCMI
>   * instance and it will be used as follows:
> @@ -182,6 +187,9 @@ struct scmi_protocol_handle {
>  	int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,
>  			u32 version);
>  	void *(*get_priv)(const struct scmi_protocol_handle *ph);
> +	int (*notifier_register)(const struct scmi_protocol_handle *ph,
> +				 u8 evt_id, const u32 *src_id,
> +				 struct notifier_block *nb);
>  };
>  
>  /**


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 04/10] uapi: Add ARM SCMI definitions
  2025-09-25 20:35 ` [PATCH 04/10] uapi: Add ARM SCMI definitions Cristian Marussi
  2025-09-26 14:30   ` kernel test robot
@ 2025-10-17 15:14   ` Jonathan Cameron
  2025-10-21  9:40     ` Cristian Marussi
  1 sibling, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-17 15:14 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:48 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Add a number of structures and ioctls definitions used by the ARM
> SCMI Telemetry protocol.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Hi Cristian,

Main thing in here is request to add __counted_by markings for the flexible array members.

>  SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) i.MX Extension Message Protocol drivers
> diff --git a/include/uapi/linux/scmi.h b/include/uapi/linux/scmi.h
> new file mode 100644
> index 000000000000..b1a6d34fee4a
> --- /dev/null
> +++ b/include/uapi/linux/scmi.h
> @@ -0,0 +1,286 @@

> +
> +/**
> + * scmi_tlm_config  - Whole instance or group configuration
> + *
> + * @enable: Enable/Disable Telemetry for the whole instance or the group
> + * @t_enable: Enable/Disable timestamping for all the DEs belonging to a group.
I'm fairly sure that even for reserved the kernel-doc script will complain if no
documentation.  (I haven't checked if it special cases that though!)
> + * @current_update_interval: Get/Set currently active update interval for the
> + *			     whole instance or a group.
> + *
> + * Used by:
> + *	RO - SCMI_TLM_GET_CFG
> + *	WO - SCMI_TLM_SET_CFG
> + *
> + * Supported by:
> + *	control/
> + *	groups/<N>/control
> + */
> +struct scmi_tlm_config {
> +	__u8 enable;
> +	__u8 t_enable;
> +	__u8 reserved[2];
> +	__u32 current_update_interval;
> +};

> +/**
> + * scmi_tlm_grps_list  - DE-groups List
> + *
> + * @num_grps: Number of entries returned in @grps
> + * @grps: An array containing descriptors for all defined DE Groups
> + *
> + * Used by:
> + *	RW - SCMI_TLM_GET_GRP_LIST
> + *
> + * Supported by:
> + *	control/
> + */
> +struct scmi_tlm_grps_list {
> +	__u32 num_grps;
> +	struct scmi_tlm_grp_info grps[];

As below on __counted_by

> +};
> +
> +/**
> + * scmi_tlm_grp_desc  - Group descriptor
> + *
> + * @num_des: Number of DEs part of this group
> + * @composing_des: An array containing the DE IDs that belongs to this group.
> + *
> + * Used by:
> + *	RW - SCMI_TLM_GET_GRP_DESC
> + *
> + * Supported by:
> + *	groups/<N>control/
> + */
> +struct scmi_tlm_grp_desc {
> +	__u32 num_des;
> +	__u32 composing_des[];

If we can give this a __counted_by marking please do.

> +};



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
  2025-09-26 17:27   ` kernel test robot
@ 2025-10-17 15:37   ` Jonathan Cameron
  2025-10-21 10:08     ` Cristian Marussi
  2025-10-28 11:43   ` Lukasz Luba
  2 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-17 15:37 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:49 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Add basic support for SCMI V4.0-alpha_0 Telemetry protocol including SHMTI,
> FastChannels, Notifications and Single Sample Reads collection methods.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

Hi,

This is very much in the superficial drive by category as reviews
go.  A few things noted but I've not looked at the code in enough
detail.

Jonathan


> diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
> new file mode 100644
> index 000000000000..f03000c173c2
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/telemetry.c
> @@ -0,0 +1,2117 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * System Control and Management Interface (SCMI) Telemetry Protocol
> + *
> + * Copyright (C) 2025 ARM Ltd.
> + *
My favorite trivial comment applies.  What does this blank line add
to readability? I'd drop it.

> + */

> +
> +struct scmi_de_desc {
> +	__le32 id;
> +	__le32 grp_id;
> +	__le32 data_sz;
> +	__le32 attr_1;
> +#define	IS_NAME_SUPPORTED(d)	((d)->attr_1 & BIT(31))
> +#define	IS_FC_SUPPORTED(d)	((d)->attr_1 & BIT(30))
> +#define	GET_DE_TYPE(d)		(le32_get_bits((d)->attr_1, GENMASK(29, 22)))
> +#define	IS_PERSISTENT(d)	((d)->attr_1 & BIT(21))
> +#define GET_DE_UNIT_EXP(d)						\
> +	({								\
> +		int __signed_exp =					\
> +			le32_get_bits((d)->attr_1, GENMASK(20, 13));	\
> +									\
> +		if (__signed_exp & BIT(7))				\
> +			__signed_exp |= GENMASK(31, 8);			\
> +		__signed_exp;						\
> +	})
> +#define	GET_DE_UNIT(d)		(le32_get_bits((d)->attr_1, GENMASK(12, 5)))
> +
> +#define GET_DE_TSTAMP_EXP(d)						\
> +	({								\
> +		int __signed_exp =					\
> +			FIELD_GET(GENMASK(4, 1), (d)->attr_1);		\
> +									\
> +		if (__signed_exp & BIT(3))				\
> +			__signed_exp |= GENMASK(31, 4);			\
> +		__signed_exp;						\
See below for sign_extend32() using code to replace these.


> +
> +struct scmi_msg_resp_telemetry_reading_complete {
> +	__le32 num_dwords;
> +	__le32 dwords[];
__counted_by(num_word);

> +};
> +
> +/* TDCF */
> +
> +#define TO_CPU_64(h, l)	(((u64)le32_to_cpu((h)) << 32) | le32_to_cpu((l)))
Some of this stuff sounds very generic and isn't at all.

Personally I think I'd just drop this one as it may be better to see
the implementation wherever it is used.

> +static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
> +					  struct payload __iomem *payld,
> +					  struct telemetry_shmti *shmti,
> +					  bool update)
> +{
> +	int used_qwords;
> +
> +	used_qwords = (USE_LINE_TS(payld) && TS_VALID(payld)) ?
> +		QWORDS_TS_LINE_DATA_PAYLD : QWORDS_LINE_DATA_PAYLD;
> +
> +	/*Invalid lines are not an error, could simply be disabled DEs */

Check for inconsistent comment syntax etc.

> +	if (DATA_INVALID(payld))
> +		return used_qwords;

> +
> +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
> +				     unsigned int shmti_id, u64 ts,
> +				     bool update)
> +{
> +	struct telemetry_shmti *shmti = &ti->shmti[shmti_id];
> +	struct tdcf __iomem *tdcf = shmti->base;
> +	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
> +	u64 startm = 0, endm = 0xffffffffffffffff;

No one likes counting fs.  Use a GENMASK probably.

> +	void *eplg = SHMTI_EPLG(shmti);


> +static void
> +scmi_telemetry_msg_payld_process(struct telemetry_info *ti,
> +				 unsigned int num_dwords, unsigned int *dwords,

I'd kind of expect something called dwords to have a fixed size. u32, u64 or
whatever.

> +				 ktime_t timestamp)
> +{
> +	u32 next = 0;
> +
> +	while (next < num_dwords) {
> +		struct payload *payld = (struct payload *)&dwords[next];
> +		struct scmi_telemetry_de *de;
> +		struct telemetry_de *tde;
> +		u32 de_id;
> +
> +		next += USE_LINE_TS(payld) ?
> +			TS_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS;
> +
> +		if (DATA_INVALID(payld)) {
> +			dev_err(ti->dev, "MSG - Received INVALID DATA line\n");
> +			continue;
> +		}
> +
> +		de_id = le32_to_cpu(payld->id);
> +		de = xa_load(&ti->xa_des, de_id);
> +		if (!de || !de->enabled) {
> +			dev_err(ti->dev,
> +				"MSG - Received INVALID DE - ID:%u  enabled:%d\n",
> +				de_id, de ? (de->enabled ? 'Y' : 'N') : 'X');
> +			continue;
> +		}
> +
> +		tde = to_tde(de);
> +		guard(mutex)(&tde->mtx);
> +		tde->cached = true;
> +		tde->last_val = LINE_DATA_GET(&payld->tsl);
> +		//TODO BLK_TS in notification payloads
> +		if (USE_LINE_TS(payld) && TS_VALID(payld))
> +			tde->last_ts = LINE_TSTAMP_GET(&payld->tsl);
> +		else
> +			tde->last_ts = 0;
> +	}
> +}


> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 59527193d6dd..6c6db95d0089 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h

...

> +#define	SCMI_TLM_GET_UPDATE_INTERVAL_SECS(x)				\
> +	(le32_get_bits((x), GENMASK(20, 5)))
Why is this one little endian specific and the next just uses assumption of
CPU Endian?

> +#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x)				\
> +	({								\
> +		int __signed_exp = FIELD_GET(GENMASK(4, 0), (x));	\
> +									\
> +		if (__signed_exp & BIT(4))				\
> +			__signed_exp |= GENMASK(31, 5);			\
sign_extend32() from bitops.h should work here and is much more self explanatory.
That would then make this something like

#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x) \
 	sign_extend32(x, 4);
or you can mask it first if you like but I don't think it makes any difference
in practice.

> +		__signed_exp;						\
> +	})
> +
> +#define SCMI_TLM_BUILD_UPDATE_INTERVAL(s, e)				    \
> +	(FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e)))

> +
> +struct scmi_telemetry_update_report {
> +	ktime_t		timestamp;
> +	unsigned int	agent_id;
> +	int		status;
> +	unsigned int	num_dwords;
> +	unsigned int	dwords[];

More places where __counted_by is appropriate. I'll not comment on any others and
just assume you'll add them wherever appropriate.

> +};

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-09-25 20:35 ` [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver Cristian Marussi
@ 2025-10-20 16:23   ` Jonathan Cameron
  2025-10-21 10:27     ` Cristian Marussi
  0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-20 16:23 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:50 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Add a new SCMI System Telemetry driver which gathers platform Telemetry
> data through the new the SCMI Telemetry protocol and expose all of the
> discovered Telemetry data events on a dedicated pseudo-filesystem that
> can be used to interactively configure SCMI Telemetry and access its
> provided data.

I'm not a fan of providing yet another filesystem but you didn't
lay out reasoning in the cover letter.

One non trivial issue is that you'll have to get filesystem review on this.
My review is rather superficial but a few things stood out.

> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

> diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> new file mode 100644
> index 000000000000..2fec465b0f33
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c


> +static ssize_t
> +scmi_tlm_update_interval_write(struct file *filp, const char __user *buf,
> +			       size_t count, loff_t *ppos)
> +{
> +	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
> +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> +	bool is_group = IS_GROUP(tlmi->cls->flags);
> +	unsigned int update_interval_ms = 0, secs = 0;
> +	int ret, grp_id, exp = -3;
> +	char *kbuf, *p, *token;
> +
> +	kbuf = memdup_user_nul(buf, count);

I'd use a __free(kfree) as then you can directly return in error paths.
Will keep the buffer around a little longer than strictly necessary but
I'm not seeing where that will cause problems.

> +	if (IS_ERR(kbuf))
> +		return PTR_ERR(kbuf);
> +
> +	p = kbuf;
> +	token = strsep(&p, " ");
> +	if (!token) {
> +		/* At least one token must exist to be a valid input */
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	ret = kstrtouint(token, 0, &secs);
> +	if (ret)
> +		goto err;
> +
> +	token = strsep(&p, " ");
> +	if (token) {
> +		ret = kstrtoint(token, 0, &exp);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	kfree(kbuf);
> +
> +	update_interval_ms = SCMI_TLM_BUILD_UPDATE_INTERVAL(secs, exp);
> +
> +	grp_id = !is_group ? SCMI_TLM_GRP_INVALID : tlmi->grp->info->id;
> +	ret = tsp->ops->collection_configure(tsp->ph, grp_id, !is_group, NULL,
> +					     &update_interval_ms, NULL);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +
> +err:
> +	kfree(kbuf);
> +	return ret;
> +}


> +
> +static int scmi_tlm_bulk_buffer_allocate_and_fill(struct scmi_tlm_inode *tlmi,
> +						  struct scmi_tlm_priv *tp)
> +{
> +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> +	const struct scmi_tlm_class *cls = tlmi->cls;
> +	struct scmi_telemetry_de_sample *samples;
> +	bool is_group = IS_GROUP(cls->flags);
> +	int ret, num_samples, res_id;
> +
> +	num_samples = !is_group ? tlmi->info->base.num_des :
> +		tlmi->grp->info->num_des;
> +	tp->buf_sz = num_samples * MAX_BULK_LINE_CHAR_LENGTH;
> +	tp->buf = kzalloc(tp->buf_sz, GFP_KERNEL);
> +	if (!tp->buf)
> +		return -ENOMEM;
> +
> +	res_id = is_group ? tlmi->grp->info->id : SCMI_TLM_GRP_INVALID;
> +	samples = kcalloc(num_samples, sizeof(*samples), GFP_KERNEL);
> +	if (!samples) {
> +		kfree(tp->buf);
> +		return -ENOMEM;
> +	}
> +
> +	ret = tp->bulk_retrieve(tsp, res_id, &num_samples, samples);
> +	if (ret) {
> +		kfree(samples);
Free them in reverse of allocation. Makes it easier to review.

> +		kfree(tp->buf);
> +		return ret;
> +	}
> +
> +	ret = scmi_tlm_buffer_fill(tsp->dev, tp->buf, tp->buf_sz, &tp->buf_len,
> +				   num_samples, samples);
I'm a little surprised by lifetime of tp->buf if this return an error.
Perhaps add a comment on that.

> +	kfree(samples);
> +
> +	return ret;
> +}


> +
> +static struct scmi_tlm_instance *scmi_tlm_init(struct scmi_tlm_setup *tsp,
> +					       int instance_id)
> +{
> +	struct device *dev = tsp->dev;
> +	struct scmi_tlm_instance *ti;
> +	int ret;
> +
> +	ti = devm_kzalloc(dev, sizeof(*ti), GFP_KERNEL);
Given use of devm I'm guessing this will only be called from probe().
With that in mind...
> +	if (!ti)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ti->info = tsp->ops->info_get(tsp->ph);
> +	if (!ti->info) {
> +		dev_err(dev, "invalid Telemetry info !\n");
> +		return ERR_PTR(-EINVAL);

		return dev_err_probe()

> +	}
> +
> +	ti->id = instance_id;
> +	ti->tsp = tsp;
> +
> +	ret = scmi_tlm_root_instance_initialize(dev, ti);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	ret = scmi_telemetry_des_initialize(dev, ti);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	ret = scmi_telemetry_groups_initialize(dev, ti);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	return ti;
> +}
> +
> +static int scmi_telemetry_probe(struct scmi_device *sdev)
> +{
> +	const struct scmi_handle *handle = sdev->handle;
> +	struct scmi_protocol_handle *ph;
> +	struct device *dev = &sdev->dev;
> +	struct scmi_tlm_instance *ti;
> +	struct scmi_tlm_setup *tsp;
> +	const void *ops;
> +
> +	if (!handle)
> +		return -ENODEV;
> +
> +	ops = handle->devm_protocol_get(sdev, sdev->protocol_id, &ph);
> +	if (IS_ERR(ops))
> +		return dev_err_probe(dev, PTR_ERR(ops),
> +				     "Cannot access protocol:0x%X\n",
> +				     sdev->protocol_id);
> +
> +	tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL);
> +	if (!tsp)
> +		return -ENOMEM;
> +
> +	tsp->dev = dev;
> +	tsp->ops = ops;
> +	tsp->ph = ph;
> +
> +	ti = scmi_tlm_init(tsp, atomic_fetch_inc(&scmi_tlm_instance_count));
> +	if (IS_ERR(ti))
> +		return PTR_ERR(ti);
> +
> +	mutex_lock(&scmi_tlm_mtx);
> +	list_add(&ti->node, &scmi_telemetry_instances);
> +	if (scmi_tlm_sb) {
> +		int ret;
> +
> +		/*
> +		 * If the file system was already mounted by the time this
> +		 * instance was probed, register explicitly, since the list
> +		 * has been scanned already.
> +		 */
> +		mutex_unlock(&scmi_tlm_mtx);
> +		ret = scmi_telemetry_instance_register(scmi_tlm_sb, ti);
> +		if (ret)
> +			return ret;
> +		mutex_lock(&scmi_tlm_mtx);
I guess this will make sense in later patches.  Right now it looks like you should
just check scmi_tlb_sb after releasing the lock.
E.g.
	mutex_lock(&scmi_tlm_mtx); //I'd spell out mutex
	list_add(&ti->ode, &scam_telemetry_instances);
	mutex_unlock(&scmi_tlm_mtx);
	if (scmi_tlm_sb) {
		ret = scmi....

> +	}
If you really have to check if (scmi_tlb_sb) under the lock just take a copy into a local
variable and use that after releasing the lock.

> +	mutex_unlock(&scmi_tlm_mtx);
> +
> +	dev_set_drvdata(&sdev->dev, ti);
> +
> +	return 0;
> +}

> +static const struct scmi_device_id scmi_id_table[] = {
> +	{ SCMI_PROTOCOL_TELEMETRY, "telemetry" },
> +	{ },

Drop that trailing comma.  Only thing it does is make
it easy to introduce a bug by putting something after it.

> +};
> +MODULE_DEVICE_TABLE(scmi, scmi_id_table);


> +
> +static int __init scmi_telemetry_init(void)
> +{
> +	int ret;
> +
> +	ret = scmi_register(&scmi_telemetry_driver);
Why do this first?  My immediate assumption is this allows
for drivers to register in parallel with the rest of init
happening.  Feels like it should be the last thin in init.

> +	if (ret)
> +		return ret;
> +
> +	ret = sysfs_create_mount_point(fs_kobj, TLM_FS_MNT);
> +	if (ret && ret != -EEXIST) {
> +		scmi_unregister(&scmi_telemetry_driver);

Given the classic pattern of building up more things to undo.
Use gotos and an error handling block as that's what we normally
expect to see in the kernel.

> +		return ret;
> +	}
> +
> +	ret = register_filesystem(&scmi_telemetry_fs);
> +	if (ret) {
> +		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
> +		scmi_unregister(&scmi_telemetry_driver);
> +	}
> +
> +	return ret;
> +}
> +module_init(scmi_telemetry_init);
> +
> +static void __exit scmi_telemetry_exit(void)
> +{
> +	int ret;
> +
> +	ret = unregister_filesystem(&scmi_telemetry_fs);

Documentation says this only fails if the filesystem isn't found.
How can that happen if the init above succeeded?

I noted from a quick scan that most filesystems don't check the
return value.  The only one that does uses it to print a message
and otherwise continues as if it succeeded.



> +	if (!ret)
> +		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
> +	else
> +		pr_err("Failed to unregister %s\n", TLM_FS_NAME);
Given 1 out of 100s of file systems bothers to do this. I'm suspecting
it's a thing that won't happen.

I'd just call the sysfs_remove_mount_point() unconditionally.

> +
> +	scmi_unregister(&scmi_telemetry_driver);
> +}
> +module_exit(scmi_telemetry_exit);
> +
> +MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
> +MODULE_DESCRIPTION("ARM SCMI Telemetry Driver");
> +MODULE_LICENSE("GPL");


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support
  2025-09-25 20:35 ` [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support Cristian Marussi
@ 2025-10-20 16:30   ` Jonathan Cameron
  2025-10-21 10:30     ` Cristian Marussi
  0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-20 16:30 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Thu, 25 Sep 2025 21:35:51 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> Extend the filesystem based interface with special 'control' file that can
> be used to configure and retrieve SCMI Telemetry data in binary form using
> the alternative ioctls-based ABI described in uapi/linux/scmi.h.
Why you say alternative.  Why do you need both?

That's in the cover letter but I'd put something here as well.
> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
>  .../firmware/arm_scmi/scmi_system_telemetry.c | 402 ++++++++++++++++++
>  1 file changed, 402 insertions(+)
> 
> diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> index 2fec465b0f33..f591aad10302 100644
> --- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> +++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c

> +static long scmi_tlm_des_read_ioctl(const struct scmi_tlm_inode *tlmi,
> +				    unsigned long arg, bool single,
> +				    bool is_group)
> +{
> +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> +	void * __user uptr = (void * __user)arg;
> +	struct scmi_tlm_data_read bulk, *bulk_ptr;
> +	int ret, grp_id = SCMI_TLM_GRP_INVALID;
> +
> +	if (copy_from_user(&bulk, uptr, sizeof(bulk)))
> +		return -EFAULT;
> +
> +	bulk_ptr = kzalloc(struct_size(bulk_ptr, samples, bulk.num_samples),

__free() would help here.

> +			   GFP_KERNEL);
> +	if (!bulk_ptr)
> +		return -ENOMEM;
> +
> +	if (is_group) {
> +		const struct scmi_telemetry_group *grp = tlmi->priv;
> +
> +		grp_id = grp->info->id;
> +	}
> +
> +	bulk_ptr->num_samples = bulk.num_samples;
> +	if (!single)
> +		ret = tsp->ops->des_bulk_read(tsp->ph, grp_id,
> +					      &bulk_ptr->num_samples,
> +			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
> +	else
> +		ret = tsp->ops->des_sample_get(tsp->ph, grp_id,
> +					       &bulk_ptr->num_samples,
> +			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);

That is very unusual code alignment.  Drag 2 lines above left to match one line above.

> +	if (ret)
> +		goto out;
> +
> +	if (copy_to_user(uptr, bulk_ptr, sizeof(*bulk_ptr) +
> +			 bulk_ptr->num_samples * sizeof(bulk_ptr->samples[0])))
> +		ret = -EFAULT;
> +
> +out:
> +	kfree(bulk_ptr);
> +
> +	return ret;
> +}


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex
  2025-10-17 15:07   ` Jonathan Cameron
@ 2025-10-21  9:36     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21  9:36 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Fri, Oct 17, 2025 at 04:07:02PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:46 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Currently the mutex dedicated to the protection of the list of registered
> > protocols is held during all the protocol initialization phase.
> > 
> > Such a wide locking region is not needed and causes problem when trying to
> > initialize notifications from within a protocol initialization routine.
> > 
> > Reduce the scope of the protocol mutex.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> 
> Hi Cristian.  A few things inline and this runs into one of the things
> that is dangerous to do with guard() or the other cleanup.h magic
> (and documented there!)

Hi Jonathan,

thanks for having a look at this series.

> 
> > ---
> >  drivers/firmware/arm_scmi/driver.c | 53 +++++++++++++++---------------
> >  1 file changed, 26 insertions(+), 27 deletions(-)
> > 
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index bd56a877fdfc..71ee25b78624 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -17,6 +17,7 @@
> >  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> >  
> >  #include <linux/bitmap.h>
> > +#include <linux/cleanup.h>
> >  #include <linux/debugfs.h>
> >  #include <linux/device.h>
> >  #include <linux/export.h>
> > @@ -2179,10 +2180,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
> >  	if (ret)
> >  		goto clean;
> >  
> > -	ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
> > -			GFP_KERNEL);
> > -	if (ret != proto->id)
> > -		goto clean;
> > +	/* Finally register the initialized protocol */
> > +	scoped_guard(mutex, &info->protocols_mtx) {
> 
> See the guidance in cleanup.h on mixing goto and anything defined in that file.
> 
> In some compilers, if you hit the goto above and hence jump over this
> the cleanup variable will still be instantiated, but not initialized leading to
> a potential attempt to unlock random memory.
> 
> Either this needs more substantial rework, or just handling the mutex with
> out using guards.
> 

Thanks for the heads-up I will dig better into cleanup.h which obviously
I did not enough...my bad.

> 
> > +		ret = idr_alloc(&info->protocols, pi, proto->id, proto->id + 1,
> > +				GFP_KERNEL);
> > +		if (ret != proto->id)
> > +			goto clean;
> > +	}
> >  
> >  	/*
> >  	 * Warn but ignore events registration errors since we do not want
> > @@ -2243,25 +2247,22 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
> >  static struct scmi_protocol_instance * __must_check
> >  scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
> >  {
> > -	struct scmi_protocol_instance *pi;
> > +	struct scmi_protocol_instance *pi = ERR_PTR(-EPROBE_DEFER);
> >  	struct scmi_info *info = handle_to_scmi_info(handle);
> > +	const struct scmi_protocol *proto;
> >  
> > -	mutex_lock(&info->protocols_mtx);
> > -	pi = idr_find(&info->protocols, protocol_id);
> > -
> > -	if (pi) {
> > -		refcount_inc(&pi->users);
> > -	} else {
> > -		const struct scmi_protocol *proto;
> > -
> > -		/* Fails if protocol not registered on bus */
> > -		proto = scmi_protocol_get(protocol_id, &info->version);
> > -		if (proto)
> > -			pi = scmi_alloc_init_protocol_instance(info, proto);
> > -		else
> > -			pi = ERR_PTR(-EPROBE_DEFER);
> > +	scoped_guard(mutex, &info->protocols_mtx) {
> > +		pi = idr_find(&info->protocols, protocol_id);
> > +		if (pi) {
> 
> if !pi we carry on with it NULL, which is a behavior change from
> before where it would be ERR_PTR(-EPROBE_DEFER);
> 
> That might not matter, but it's not 'obviously' a safe change.

You are right...also the Dox comment is obsoleted..I will review this
patch as a whole, since even if probably right in its essence is badly
implemented because I rushed it in in this series to fix a probblem that
popped up on KASAN.

> 
> > +			refcount_inc(&pi->users);
> > +			return pi;
> > +		}
> >  	}
> > -	mutex_unlock(&info->protocols_mtx);
> > +
> > +	/* Fails if protocol not registered on bus */
> > +	proto = scmi_protocol_get(protocol_id, &info->version);
> > +	if (proto)
> Trivial but I'd flip the logic to
> 	if (!proto)
> 		return ERR_PTR(-EPROBE_DEFER);
> assuming a NULL return as mentioned above isn't the intent.
> Then
> 	return scmi_alloc_init_protocol_instance(info, protoo);

Yes this seems a better rework...

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications
  2025-10-17 15:10   ` Jonathan Cameron
@ 2025-10-21  9:37     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21  9:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Fri, Oct 17, 2025 at 04:10:14PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:47 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Allow protocols themselves to register for their own notifications and
> > providing their own notifier callbacks. While at that, allow for a protocol
> > to register events with compilation-time unknown report/event sizes: such
> > events will use the maximum transport size.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> Hi Cristian,
>

Hi,
 
> A few drive by comments...
> 
> > diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
> > index 78e9e27dc9ec..3e623c14745d 100644
> > --- a/drivers/firmware/arm_scmi/notify.c
> > +++ b/drivers/firmware/arm_scmi/notify.c
> > @@ -593,7 +593,12 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
> >  	if (!r_evt)
> >  		return -EINVAL;
> >  
> > -	if (len > r_evt->evt->max_payld_sz) {
> > +	/* Events with a zero max_payld_sz are sized to be of the maximum
> Local multiline comment syntax seems to be
> 	/*
> 	 * Events...

Yes of course...

> 
> > +	 * size allowed by the transport: no need to be size-checked here
> > +	 * since the transport layer would have already dropped such
> > +	 * over-sized messages.
> > +	 */
> > +	if (r_evt->evt->max_payld_sz && len > r_evt->evt->max_payld_sz) {
> 
> > diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
> > index d62c4469d1fd..2e40a7bb5b01 100644
> > --- a/drivers/firmware/arm_scmi/protocols.h
> > +++ b/drivers/firmware/arm_scmi/protocols.h
> > @@ -161,8 +161,13 @@ struct scmi_proto_helpers_ops;
> >   * @dev: A reference to the associated SCMI instance device (handle->dev).
> >   * @xops: A reference to a struct holding refs to the core xfer operations that
> >   *	  can be used by the protocol implementation to generate SCMI messages.
> > + * @hops: A reference to a struct holding refs to the common helper operations
> > + *	  that can be used by the protocol implementation.
> 
> @hops isn't added in this patch so either it should be handled in where it was
> added, or if that was missed a precursor patch to this one.

Indeed.

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 04/10] uapi: Add ARM SCMI definitions
  2025-10-17 15:14   ` Jonathan Cameron
@ 2025-10-21  9:40     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21  9:40 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Fri, Oct 17, 2025 at 04:14:01PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:48 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Add a number of structures and ioctls definitions used by the ARM
> > SCMI Telemetry protocol.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> Hi Cristian,
> 

Hi,

> Main thing in here is request to add __counted_by markings for the flexible array members.

...right...missed that completely...

> 
> >  SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) i.MX Extension Message Protocol drivers
> > diff --git a/include/uapi/linux/scmi.h b/include/uapi/linux/scmi.h
> > new file mode 100644
> > index 000000000000..b1a6d34fee4a
> > --- /dev/null
> > +++ b/include/uapi/linux/scmi.h
> > @@ -0,0 +1,286 @@
> 
> > +
> > +/**
> > + * scmi_tlm_config  - Whole instance or group configuration
> > + *
> > + * @enable: Enable/Disable Telemetry for the whole instance or the group
> > + * @t_enable: Enable/Disable timestamping for all the DEs belonging to a group.
> I'm fairly sure that even for reserved the kernel-doc script will complain if no
> documentation.  (I haven't checked if it special cases that though!)

Right..

> > + * @current_update_interval: Get/Set currently active update interval for the
> > + *			     whole instance or a group.
> > + *
> > + * Used by:
> > + *	RO - SCMI_TLM_GET_CFG
> > + *	WO - SCMI_TLM_SET_CFG
> > + *
> > + * Supported by:
> > + *	control/
> > + *	groups/<N>/control
> > + */
> > +struct scmi_tlm_config {
> > +	__u8 enable;
> > +	__u8 t_enable;
> > +	__u8 reserved[2];
> > +	__u32 current_update_interval;
> > +};
> 
> > +/**
> > + * scmi_tlm_grps_list  - DE-groups List
> > + *
> > + * @num_grps: Number of entries returned in @grps
> > + * @grps: An array containing descriptors for all defined DE Groups
> > + *
> > + * Used by:
> > + *	RW - SCMI_TLM_GET_GRP_LIST
> > + *
> > + * Supported by:
> > + *	control/
> > + */
> > +struct scmi_tlm_grps_list {
> > +	__u32 num_grps;
> > +	struct scmi_tlm_grp_info grps[];
> 
> As below on __counted_by

I will add.

> 
> > +};
> > +
> > +/**
> > + * scmi_tlm_grp_desc  - Group descriptor
> > + *
> > + * @num_des: Number of DEs part of this group
> > + * @composing_des: An array containing the DE IDs that belongs to this group.
> > + *
> > + * Used by:
> > + *	RW - SCMI_TLM_GET_GRP_DESC
> > + *
> > + * Supported by:
> > + *	groups/<N>control/
> > + */
> > +struct scmi_tlm_grp_desc {
> > +	__u32 num_des;
> > +	__u32 composing_des[];
> 
> If we can give this a __counted_by marking please do.
>

I will.

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-10-17 15:37   ` Jonathan Cameron
@ 2025-10-21 10:08     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21 10:08 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Fri, Oct 17, 2025 at 04:37:25PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:49 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Add basic support for SCMI V4.0-alpha_0 Telemetry protocol including SHMTI,
> > FastChannels, Notifications and Single Sample Reads collection methods.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> 
> Hi,
> 

Hi thanks for having a look

> This is very much in the superficial drive by category as reviews
> go.  A few things noted but I've not looked at the code in enough
> detail.

...this is still very early days as a series since I moved across a few
different implementations in the previous RFCs, so as noted in the
cover-letter there are in general lots of open-issues...

...BUT I am sure there will be more after this review :P

Thanks for the feedback in the meantime.

> 
> Jonathan
> 
> 
> > diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
> > new file mode 100644
> > index 000000000000..f03000c173c2
> > --- /dev/null
> > +++ b/drivers/firmware/arm_scmi/telemetry.c
> > @@ -0,0 +1,2117 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * System Control and Management Interface (SCMI) Telemetry Protocol
> > + *
> > + * Copyright (C) 2025 ARM Ltd.
> > + *
> My favorite trivial comment applies.  What does this blank line add
> to readability? I'd drop it.
> 

... I would say..at the moment felt right :P ... but there is no need
and I wil drop it.

> > + */
> 
> > +
> > +struct scmi_de_desc {
> > +	__le32 id;
> > +	__le32 grp_id;
> > +	__le32 data_sz;
> > +	__le32 attr_1;
> > +#define	IS_NAME_SUPPORTED(d)	((d)->attr_1 & BIT(31))
> > +#define	IS_FC_SUPPORTED(d)	((d)->attr_1 & BIT(30))
> > +#define	GET_DE_TYPE(d)		(le32_get_bits((d)->attr_1, GENMASK(29, 22)))
> > +#define	IS_PERSISTENT(d)	((d)->attr_1 & BIT(21))
> > +#define GET_DE_UNIT_EXP(d)						\
> > +	({								\
> > +		int __signed_exp =					\
> > +			le32_get_bits((d)->attr_1, GENMASK(20, 13));	\
> > +									\
> > +		if (__signed_exp & BIT(7))				\
> > +			__signed_exp |= GENMASK(31, 8);			\
> > +		__signed_exp;						\
> > +	})
> > +#define	GET_DE_UNIT(d)		(le32_get_bits((d)->attr_1, GENMASK(12, 5)))
> > +
> > +#define GET_DE_TSTAMP_EXP(d)						\
> > +	({								\
> > +		int __signed_exp =					\
> > +			FIELD_GET(GENMASK(4, 1), (d)->attr_1);		\
> > +									\
> > +		if (__signed_exp & BIT(3))				\
> > +			__signed_exp |= GENMASK(31, 4);			\
> > +		__signed_exp;						\
> See below for sign_extend32() using code to replace these.
> 

Sadly enough, in the past I am sure I have also searched for something similar
and did not find it despite being merged since 2010 apparently... :<

> 
> > +
> > +struct scmi_msg_resp_telemetry_reading_complete {
> > +	__le32 num_dwords;
> > +	__le32 dwords[];
> __counted_by(num_word);
>

Mmmm, this is really used to cast to a variable sized received message
payload..is the __counted_by gonna work as intended in this case ?
..because this means a bad sizing could be triggered by a bad FW...

(I suppose I will find my answer looking better at __counted_by inner workings...)

I will check anyway the processing of this var size message...
 
> > +};
> > +
> > +/* TDCF */
> > +
> > +#define TO_CPU_64(h, l)	(((u64)le32_to_cpu((h)) << 32) | le32_to_cpu((l)))
> Some of this stuff sounds very generic and isn't at all.
> 
> Personally I think I'd just drop this one as it may be better to see
> the implementation wherever it is used.

Ok ...it was to avoid a bit of duplication in some macros down below
and made the intent more clear from the name..

> 
> > +static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
> > +					  struct payload __iomem *payld,
> > +					  struct telemetry_shmti *shmti,
> > +					  bool update)
> > +{
> > +	int used_qwords;
> > +
> > +	used_qwords = (USE_LINE_TS(payld) && TS_VALID(payld)) ?
> > +		QWORDS_TS_LINE_DATA_PAYLD : QWORDS_LINE_DATA_PAYLD;
> > +
> > +	/*Invalid lines are not an error, could simply be disabled DEs */
> 
> Check for inconsistent comment syntax etc.

Ok.

> 
> > +	if (DATA_INVALID(payld))
> > +		return used_qwords;
> 
> > +
> > +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
> > +				     unsigned int shmti_id, u64 ts,
> > +				     bool update)
> > +{
> > +	struct telemetry_shmti *shmti = &ti->shmti[shmti_id];
> > +	struct tdcf __iomem *tdcf = shmti->base;
> > +	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
> > +	u64 startm = 0, endm = 0xffffffffffffffff;
> 
> No one likes counting fs.  Use a GENMASK probably.

Yes definitely better, even though is not really a mask but just a fixed
invalid value to use at start...

> 
> > +	void *eplg = SHMTI_EPLG(shmti);
> 
> 
> > +static void
> > +scmi_telemetry_msg_payld_process(struct telemetry_info *ti,
> > +				 unsigned int num_dwords, unsigned int *dwords,
> 
> I'd kind of expect something called dwords to have a fixed size. u32, u64 or
> whatever.

Yes I agree.

> 
> > +				 ktime_t timestamp)
> > +{
> > +	u32 next = 0;
> > +
> > +	while (next < num_dwords) {
> > +		struct payload *payld = (struct payload *)&dwords[next];
> > +		struct scmi_telemetry_de *de;
> > +		struct telemetry_de *tde;
> > +		u32 de_id;
> > +
> > +		next += USE_LINE_TS(payld) ?
> > +			TS_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS;
> > +
> > +		if (DATA_INVALID(payld)) {
> > +			dev_err(ti->dev, "MSG - Received INVALID DATA line\n");
> > +			continue;
> > +		}
> > +
> > +		de_id = le32_to_cpu(payld->id);
> > +		de = xa_load(&ti->xa_des, de_id);
> > +		if (!de || !de->enabled) {
> > +			dev_err(ti->dev,
> > +				"MSG - Received INVALID DE - ID:%u  enabled:%d\n",
> > +				de_id, de ? (de->enabled ? 'Y' : 'N') : 'X');
> > +			continue;
> > +		}
> > +
> > +		tde = to_tde(de);
> > +		guard(mutex)(&tde->mtx);
> > +		tde->cached = true;
> > +		tde->last_val = LINE_DATA_GET(&payld->tsl);
> > +		//TODO BLK_TS in notification payloads
> > +		if (USE_LINE_TS(payld) && TS_VALID(payld))
> > +			tde->last_ts = LINE_TSTAMP_GET(&payld->tsl);
> > +		else
> > +			tde->last_ts = 0;
> > +	}
> > +}
> 
> 
> > diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> > index 59527193d6dd..6c6db95d0089 100644
> > --- a/include/linux/scmi_protocol.h
> > +++ b/include/linux/scmi_protocol.h
> 
> ...
> 
> > +#define	SCMI_TLM_GET_UPDATE_INTERVAL_SECS(x)				\
> > +	(le32_get_bits((x), GENMASK(20, 5)))
> Why is this one little endian specific and the next just uses assumption of
> CPU Endian?

Mmmm is not becasue the next one extract a 5 bit value that being < 8bit
has no endianity issue ?

....haviong said that...I think this version has a lot of endian issue to be
fixed as pointed out also by the kernel bots...

> 
> > +#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x)				\
> > +	({								\
> > +		int __signed_exp = FIELD_GET(GENMASK(4, 0), (x));	\
> > +									\
> > +		if (__signed_exp & BIT(4))				\
> > +			__signed_exp |= GENMASK(31, 5);			\
> sign_extend32() from bitops.h should work here and is much more self explanatory.
> That would then make this something like
> 
> #define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x) \
>  	sign_extend32(x, 4);
> or you can mask it first if you like but I don't think it makes any difference
> in practice.

Yes using well known helpers is much better...I will rework and
test...just in case :D

> 
> > +		__signed_exp;						\
> > +	})
> > +
> > +#define SCMI_TLM_BUILD_UPDATE_INTERVAL(s, e)				    \
> > +	(FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e)))
> 
> > +
> > +struct scmi_telemetry_update_report {
> > +	ktime_t		timestamp;
> > +	unsigned int	agent_id;
> > +	int		status;
> > +	unsigned int	num_dwords;
> > +	unsigned int	dwords[];
> 
> More places where __counted_by is appropriate. I'll not comment on any others and
> just assume you'll add them wherever appropriate.
> 

Sure, but, as said above, I will reason a bit on the places where the struct is used
to cast a received FW payload (which is NOT the case in this latter example...)

Thanks,
Crisian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-10-20 16:23   ` Jonathan Cameron
@ 2025-10-21 10:27     ` Cristian Marussi
  2025-10-21 15:15       ` Jonathan Cameron
  0 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21 10:27 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Mon, Oct 20, 2025 at 05:23:28PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:50 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Add a new SCMI System Telemetry driver which gathers platform Telemetry
> > data through the new the SCMI Telemetry protocol and expose all of the
> > discovered Telemetry data events on a dedicated pseudo-filesystem that
> > can be used to interactively configure SCMI Telemetry and access its
> > provided data.
>

Hi,
 
> I'm not a fan of providing yet another filesystem but you didn't
> lay out reasoning in the cover letter.

Sorry, I dont understand..you mean here that I did NOT provide enough reasons
why I am adopting a new FS approach ? ... or I misunderstood the English ?

.. because I did provide a lot of reasons (for my point-of-view) to go
for a new FS in the cover-letter...

> 
> One non trivial issue is that you'll have to get filesystem review on this.
> My review is rather superficial but a few things stood out.

Well yes I would have expected that, but now the FS implementation
internals of this series is definetely immature and to be reworked (to
the extent of using a well-know deprecated FS mount api at first..)

So I posted this V1 to lay-out the ideas and the effective FS API layout
but I was planning to extend the review audience once I have reworked fully
the series FS bits in the next V2...

> 
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> 
> > diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> > new file mode 100644
> > index 000000000000..2fec465b0f33
> > --- /dev/null
> > +++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> 
> 
> > +static ssize_t
> > +scmi_tlm_update_interval_write(struct file *filp, const char __user *buf,
> > +			       size_t count, loff_t *ppos)
> > +{
> > +	struct scmi_tlm_inode *tlmi = to_tlm_inode(file_inode(filp));
> > +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> > +	bool is_group = IS_GROUP(tlmi->cls->flags);
> > +	unsigned int update_interval_ms = 0, secs = 0;
> > +	int ret, grp_id, exp = -3;
> > +	char *kbuf, *p, *token;
> > +
> > +	kbuf = memdup_user_nul(buf, count);
> 
> I'd use a __free(kfree) as then you can directly return in error paths.
> Will keep the buffer around a little longer than strictly necessary but
> I'm not seeing where that will cause problems.
> 

Ok.

> > +	if (IS_ERR(kbuf))
> > +		return PTR_ERR(kbuf);
> > +
> > +	p = kbuf;
> > +	token = strsep(&p, " ");
> > +	if (!token) {
> > +		/* At least one token must exist to be a valid input */
> > +		ret = -EINVAL;
> > +		goto err;
> > +	}
> > +
> > +	ret = kstrtouint(token, 0, &secs);
> > +	if (ret)
> > +		goto err;
> > +
> > +	token = strsep(&p, " ");
> > +	if (token) {
> > +		ret = kstrtoint(token, 0, &exp);
> > +		if (ret)
> > +			goto err;
> > +	}
> > +
> > +	kfree(kbuf);
> > +
> > +	update_interval_ms = SCMI_TLM_BUILD_UPDATE_INTERVAL(secs, exp);
> > +
> > +	grp_id = !is_group ? SCMI_TLM_GRP_INVALID : tlmi->grp->info->id;
> > +	ret = tsp->ops->collection_configure(tsp->ph, grp_id, !is_group, NULL,
> > +					     &update_interval_ms, NULL);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return count;
> > +
> > +err:
> > +	kfree(kbuf);
> > +	return ret;
> > +}
> 
> 
> > +
> > +static int scmi_tlm_bulk_buffer_allocate_and_fill(struct scmi_tlm_inode *tlmi,
> > +						  struct scmi_tlm_priv *tp)
> > +{
> > +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> > +	const struct scmi_tlm_class *cls = tlmi->cls;
> > +	struct scmi_telemetry_de_sample *samples;
> > +	bool is_group = IS_GROUP(cls->flags);
> > +	int ret, num_samples, res_id;
> > +
> > +	num_samples = !is_group ? tlmi->info->base.num_des :
> > +		tlmi->grp->info->num_des;
> > +	tp->buf_sz = num_samples * MAX_BULK_LINE_CHAR_LENGTH;
> > +	tp->buf = kzalloc(tp->buf_sz, GFP_KERNEL);
> > +	if (!tp->buf)
> > +		return -ENOMEM;
> > +
> > +	res_id = is_group ? tlmi->grp->info->id : SCMI_TLM_GRP_INVALID;
> > +	samples = kcalloc(num_samples, sizeof(*samples), GFP_KERNEL);
> > +	if (!samples) {
> > +		kfree(tp->buf);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	ret = tp->bulk_retrieve(tsp, res_id, &num_samples, samples);
> > +	if (ret) {
> > +		kfree(samples);
> Free them in reverse of allocation. Makes it easier to review.
> 

Ok.

> > +		kfree(tp->buf);
> > +		return ret;
> > +	}
> > +
> > +	ret = scmi_tlm_buffer_fill(tsp->dev, tp->buf, tp->buf_sz, &tp->buf_len,
> > +				   num_samples, samples);
> I'm a little surprised by lifetime of tp->buf if this return an error.
> Perhaps add a comment on that.

Ok.

> 
> > +	kfree(samples);
> > +
> > +	return ret;
> > +}
> 
> 
> > +
> > +static struct scmi_tlm_instance *scmi_tlm_init(struct scmi_tlm_setup *tsp,
> > +					       int instance_id)
> > +{
> > +	struct device *dev = tsp->dev;
> > +	struct scmi_tlm_instance *ti;
> > +	int ret;
> > +
> > +	ti = devm_kzalloc(dev, sizeof(*ti), GFP_KERNEL);
> Given use of devm I'm guessing this will only be called from probe().
> With that in mind...

Yes...

> > +	if (!ti)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	ti->info = tsp->ops->info_get(tsp->ph);
> > +	if (!ti->info) {
> > +		dev_err(dev, "invalid Telemetry info !\n");
> > +		return ERR_PTR(-EINVAL);
> 
> 		return dev_err_probe()
> 

Indeed.

> > +	}
> > +
> > +	ti->id = instance_id;
> > +	ti->tsp = tsp;
> > +
> > +	ret = scmi_tlm_root_instance_initialize(dev, ti);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	ret = scmi_telemetry_des_initialize(dev, ti);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	ret = scmi_telemetry_groups_initialize(dev, ti);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	return ti;
> > +}
> > +
> > +static int scmi_telemetry_probe(struct scmi_device *sdev)
> > +{
> > +	const struct scmi_handle *handle = sdev->handle;
> > +	struct scmi_protocol_handle *ph;
> > +	struct device *dev = &sdev->dev;
> > +	struct scmi_tlm_instance *ti;
> > +	struct scmi_tlm_setup *tsp;
> > +	const void *ops;
> > +
> > +	if (!handle)
> > +		return -ENODEV;
> > +
> > +	ops = handle->devm_protocol_get(sdev, sdev->protocol_id, &ph);
> > +	if (IS_ERR(ops))
> > +		return dev_err_probe(dev, PTR_ERR(ops),
> > +				     "Cannot access protocol:0x%X\n",
> > +				     sdev->protocol_id);
> > +
> > +	tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL);
> > +	if (!tsp)
> > +		return -ENOMEM;
> > +
> > +	tsp->dev = dev;
> > +	tsp->ops = ops;
> > +	tsp->ph = ph;
> > +
> > +	ti = scmi_tlm_init(tsp, atomic_fetch_inc(&scmi_tlm_instance_count));
> > +	if (IS_ERR(ti))
> > +		return PTR_ERR(ti);
> > +
> > +	mutex_lock(&scmi_tlm_mtx);
> > +	list_add(&ti->node, &scmi_telemetry_instances);
> > +	if (scmi_tlm_sb) {
> > +		int ret;
> > +
> > +		/*
> > +		 * If the file system was already mounted by the time this
> > +		 * instance was probed, register explicitly, since the list
> > +		 * has been scanned already.
> > +		 */
> > +		mutex_unlock(&scmi_tlm_mtx);
> > +		ret = scmi_telemetry_instance_register(scmi_tlm_sb, ti);
> > +		if (ret)
> > +			return ret;
> > +		mutex_lock(&scmi_tlm_mtx);
> I guess this will make sense in later patches.  Right now it looks like you should
> just check scmi_tlb_sb after releasing the lock.
> E.g.
> 	mutex_lock(&scmi_tlm_mtx); //I'd spell out mutex
> 	list_add(&ti->ode, &scam_telemetry_instances);
> 	mutex_unlock(&scmi_tlm_mtx);
> 	if (scmi_tlm_sb) {
> 		ret = scmi....
> 
> > +	}
> If you really have to check if (scmi_tlb_sb) under the lock just take a copy into a local
> variable and use that after releasing the lock.
> 


Yes a lot to review/rework here...

> > +	mutex_unlock(&scmi_tlm_mtx);
> > +
> > +	dev_set_drvdata(&sdev->dev, ti);
> > +
> > +	return 0;
> > +}
> 
> > +static const struct scmi_device_id scmi_id_table[] = {
> > +	{ SCMI_PROTOCOL_TELEMETRY, "telemetry" },
> > +	{ },
> 
> Drop that trailing comma.  Only thing it does is make
> it easy to introduce a bug by putting something after it.
> 

I'll do.

> > +};
> > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> 
> 
> > +
> > +static int __init scmi_telemetry_init(void)
> > +{
> > +	int ret;
> > +
> > +	ret = scmi_register(&scmi_telemetry_driver);
> Why do this first?  My immediate assumption is this allows
> for drivers to register in parallel with the rest of init
> happening.  Feels like it should be the last thin in init.
> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = sysfs_create_mount_point(fs_kobj, TLM_FS_MNT);
> > +	if (ret && ret != -EEXIST) {
> > +		scmi_unregister(&scmi_telemetry_driver);
> 
> Given the classic pattern of building up more things to undo.
> Use gotos and an error handling block as that's what we normally
> expect to see in the kernel.

Ok.

> 
> > +		return ret;
> > +	}
> > +
> > +	ret = register_filesystem(&scmi_telemetry_fs);
> > +	if (ret) {
> > +		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
> > +		scmi_unregister(&scmi_telemetry_driver);
> > +	}
> > +
> > +	return ret;
> > +}
> > +module_init(scmi_telemetry_init);
> > +
> > +static void __exit scmi_telemetry_exit(void)
> > +{
> > +	int ret;
> > +
> > +	ret = unregister_filesystem(&scmi_telemetry_fs);
> 
> Documentation says this only fails if the filesystem isn't found.
> How can that happen if the init above succeeded?
> 
> I noted from a quick scan that most filesystems don't check the
> return value.  The only one that does uses it to print a message
> and otherwise continues as if it succeeded.

Ok.

> 
> 
> 
> > +	if (!ret)
> > +		sysfs_remove_mount_point(fs_kobj, TLM_FS_MNT);
> > +	else
> > +		pr_err("Failed to unregister %s\n", TLM_FS_NAME);
> Given 1 out of 100s of file systems bothers to do this. I'm suspecting
> it's a thing that won't happen.
> 
> I'd just call the sysfs_remove_mount_point() unconditionally.
> 

Ok.

I wil rework heavily in V2 the FS layer and post also to the related FS
mailing lists...

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support
  2025-10-20 16:30   ` Jonathan Cameron
@ 2025-10-21 10:30     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21 10:30 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Mon, Oct 20, 2025 at 05:30:34PM +0100, Jonathan Cameron wrote:
> On Thu, 25 Sep 2025 21:35:51 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > Extend the filesystem based interface with special 'control' file that can
> > be used to configure and retrieve SCMI Telemetry data in binary form using
> > the alternative ioctls-based ABI described in uapi/linux/scmi.h.
> Why you say alternative.  Why do you need both?
> 
> That's in the cover letter but I'd put something here as well.

Ok..indeed all my babbling in teh cover wont be anywhere once merged.

> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> >  .../firmware/arm_scmi/scmi_system_telemetry.c | 402 ++++++++++++++++++
> >  1 file changed, 402 insertions(+)
> > 
> > diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> > index 2fec465b0f33..f591aad10302 100644
> > --- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> > +++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
> 
> > +static long scmi_tlm_des_read_ioctl(const struct scmi_tlm_inode *tlmi,
> > +				    unsigned long arg, bool single,
> > +				    bool is_group)
> > +{
> > +	const struct scmi_tlm_setup *tsp = tlmi->tsp;
> > +	void * __user uptr = (void * __user)arg;
> > +	struct scmi_tlm_data_read bulk, *bulk_ptr;
> > +	int ret, grp_id = SCMI_TLM_GRP_INVALID;
> > +
> > +	if (copy_from_user(&bulk, uptr, sizeof(bulk)))
> > +		return -EFAULT;
> > +
> > +	bulk_ptr = kzalloc(struct_size(bulk_ptr, samples, bulk.num_samples),
> 
> __free() would help here.
> 

Ok.

> > +			   GFP_KERNEL);
> > +	if (!bulk_ptr)
> > +		return -ENOMEM;
> > +
> > +	if (is_group) {
> > +		const struct scmi_telemetry_group *grp = tlmi->priv;
> > +
> > +		grp_id = grp->info->id;
> > +	}
> > +
> > +	bulk_ptr->num_samples = bulk.num_samples;
> > +	if (!single)
> > +		ret = tsp->ops->des_bulk_read(tsp->ph, grp_id,
> > +					      &bulk_ptr->num_samples,
> > +			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
> > +	else
> > +		ret = tsp->ops->des_sample_get(tsp->ph, grp_id,
> > +					       &bulk_ptr->num_samples,
> > +			  (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
> 
> That is very unusual code alignment.  Drag 2 lines above left to match one line above.

Trying to please checkpatch while staying under 80 cols...BUT I know is NOT a
good reason for the above mess...

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-10-21 10:27     ` Cristian Marussi
@ 2025-10-21 15:15       ` Jonathan Cameron
  2025-10-21 16:03         ` Cristian Marussi
  0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-21 15:15 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Tue, 21 Oct 2025 11:27:02 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> On Mon, Oct 20, 2025 at 05:23:28PM +0100, Jonathan Cameron wrote:
> > On Thu, 25 Sep 2025 21:35:50 +0100
> > Cristian Marussi <cristian.marussi@arm.com> wrote:
> >   
> > > Add a new SCMI System Telemetry driver which gathers platform Telemetry
> > > data through the new the SCMI Telemetry protocol and expose all of the
> > > discovered Telemetry data events on a dedicated pseudo-filesystem that
> > > can be used to interactively configure SCMI Telemetry and access its
> > > provided data.  
> >  
> 
> Hi,
>  
> > I'm not a fan of providing yet another filesystem but you didn't

"did" was what this was meant to say.

Sorry for the confusing garbage comment from me!

> > lay out reasoning in the cover letter.  
> 
> Sorry, I dont understand..you mean here that I did NOT provide enough reasons
> why I am adopting a new FS approach ? ... or I misunderstood the English ?
> 
> .. because I did provide a lot of reasons (for my point-of-view) to go
> for a new FS in the cover-letter...
> 
> > 
> > One non trivial issue is that you'll have to get filesystem review on this.
> > My review is rather superficial but a few things stood out.  
> 
> Well yes I would have expected that, but now the FS implementation
> internals of this series is definetely immature and to be reworked (to
> the extent of using a well-know deprecated FS mount api at first..)
> 
> So I posted this V1 to lay-out the ideas and the effective FS API layout
> but I was planning to extend the review audience once I have reworked fully
> the series FS bits in the next V2...

I'd suggest ABI docs for v2. That will match what you have in the cover letter
but put it in the somewhat formal description format of Documentation/ABI/


THanks,

Jonathan

> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-10-21 15:15       ` Jonathan Cameron
@ 2025-10-21 16:03         ` Cristian Marussi
  2025-10-24 10:33           ` Jonathan Cameron
  0 siblings, 1 reply; 32+ messages in thread
From: Cristian Marussi @ 2025-10-21 16:03 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, quic_sibis,
	dan.carpenter, d-gole, souvik.chakravarty

On Tue, Oct 21, 2025 at 04:15:29PM +0100, Jonathan Cameron wrote:
> On Tue, 21 Oct 2025 11:27:02 +0100
> Cristian Marussi <cristian.marussi@arm.com> wrote:
> 
> > On Mon, Oct 20, 2025 at 05:23:28PM +0100, Jonathan Cameron wrote:
> > > On Thu, 25 Sep 2025 21:35:50 +0100
> > > Cristian Marussi <cristian.marussi@arm.com> wrote:
> > >   
> > > > Add a new SCMI System Telemetry driver which gathers platform Telemetry
> > > > data through the new the SCMI Telemetry protocol and expose all of the
> > > > discovered Telemetry data events on a dedicated pseudo-filesystem that
> > > > can be used to interactively configure SCMI Telemetry and access its
> > > > provided data.  
> > >  
> > 
> > Hi,
> >  
> > > I'm not a fan of providing yet another filesystem but you didn't
> 
> "did" was what this was meant to say.
> 
> Sorry for the confusing garbage comment from me!
> 
> > > lay out reasoning in the cover letter.  
> > 
> > Sorry, I dont understand..you mean here that I did NOT provide enough reasons
> > why I am adopting a new FS approach ? ... or I misunderstood the English ?
> > 
> > .. because I did provide a lot of reasons (for my point-of-view) to go
> > for a new FS in the cover-letter...
> > 
> > > 
> > > One non trivial issue is that you'll have to get filesystem review on this.
> > > My review is rather superficial but a few things stood out.  
> > 
> > Well yes I would have expected that, but now the FS implementation
> > internals of this series is definetely immature and to be reworked (to
> > the extent of using a well-know deprecated FS mount api at first..)
> > 
> > So I posted this V1 to lay-out the ideas and the effective FS API layout
> > but I was planning to extend the review audience once I have reworked fully
> > the series FS bits in the next V2...
> 
> I'd suggest ABI docs for v2. That will match what you have in the cover letter
> but put it in the somewhat formal description format of Documentation/ABI/
> 

Oh yes of course... the while docs/ stuff is still TBD...btw I am not even
sure if the whole driver will be required to be moved into fs/ as a
requirement while doing filesystem review...I suppose I will leave this
sort of reworks for the next reviews cycles....

...and...if I may ask... is it linux-fsdevel the ML for this fs-related
stuff I suppose...not sure about maintainers looking at MAINTAINERS ...

Thanks a lot for having a look Jonathan.
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver
  2025-10-21 16:03         ` Cristian Marussi
@ 2025-10-24 10:33           ` Jonathan Cameron
  0 siblings, 0 replies; 32+ messages in thread
From: Jonathan Cameron @ 2025-10-24 10:33 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, sudeep.holla,
	james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty

On Tue, 21 Oct 2025 17:03:36 +0100
Cristian Marussi <cristian.marussi@arm.com> wrote:

> On Tue, Oct 21, 2025 at 04:15:29PM +0100, Jonathan Cameron wrote:
> > On Tue, 21 Oct 2025 11:27:02 +0100
> > Cristian Marussi <cristian.marussi@arm.com> wrote:
> >   
> > > On Mon, Oct 20, 2025 at 05:23:28PM +0100, Jonathan Cameron wrote:  
> > > > On Thu, 25 Sep 2025 21:35:50 +0100
> > > > Cristian Marussi <cristian.marussi@arm.com> wrote:
> > > >     
> > > > > Add a new SCMI System Telemetry driver which gathers platform Telemetry
> > > > > data through the new the SCMI Telemetry protocol and expose all of the
> > > > > discovered Telemetry data events on a dedicated pseudo-filesystem that
> > > > > can be used to interactively configure SCMI Telemetry and access its
> > > > > provided data.    
> > > >    
> > > 
> > > Hi,
> > >    
> > > > I'm not a fan of providing yet another filesystem but you didn't  
> > 
> > "did" was what this was meant to say.
> > 
> > Sorry for the confusing garbage comment from me!
> >   
> > > > lay out reasoning in the cover letter.    
> > > 
> > > Sorry, I dont understand..you mean here that I did NOT provide enough reasons
> > > why I am adopting a new FS approach ? ... or I misunderstood the English ?
> > > 
> > > .. because I did provide a lot of reasons (for my point-of-view) to go
> > > for a new FS in the cover-letter...
> > >   
> > > > 
> > > > One non trivial issue is that you'll have to get filesystem review on this.
> > > > My review is rather superficial but a few things stood out.    
> > > 
> > > Well yes I would have expected that, but now the FS implementation
> > > internals of this series is definetely immature and to be reworked (to
> > > the extent of using a well-know deprecated FS mount api at first..)
> > > 
> > > So I posted this V1 to lay-out the ideas and the effective FS API layout
> > > but I was planning to extend the review audience once I have reworked fully
> > > the series FS bits in the next V2...  
> > 
> > I'd suggest ABI docs for v2. That will match what you have in the cover letter
> > but put it in the somewhat formal description format of Documentation/ABI/
> >   
> 
> Oh yes of course... the while docs/ stuff is still TBD...btw I am not even
> sure if the whole driver will be required to be moved into fs/ as a
> requirement while doing filesystem review...I suppose I will leave this
> sort of reworks for the next reviews cycles....
> 
> ...and...if I may ask... is it linux-fsdevel the ML for this fs-related
> stuff I suppose...not sure about maintainers looking at MAINTAINERS ...

Seems resonable but beyond that I have no idea.

Give it a go and see what happens.  Probably also include kernfs related folk
directly. They are likely to have opinions and might review if they have time.


Jonathan
> 
> Thanks a lot for having a look Jonathan.
> Cristian
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
  2025-09-26 17:27   ` kernel test robot
  2025-10-17 15:37   ` Jonathan Cameron
@ 2025-10-28 11:43   ` Lukasz Luba
  2025-10-28 17:51     ` Cristian Marussi
  2 siblings, 1 reply; 32+ messages in thread
From: Lukasz Luba @ 2025-10-28 11:43 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: sudeep.holla, james.quinlan, linux-arm-kernel, linux-kernel,
	f.fainelli, vincent.guittot, etienne.carriere, peng.fan,
	michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty, arm-scmi

Hi Cristian,

This is a first attempt for the review, because there is so much code.
I might find something in the next step, because for now I still
haven't built full picture of it...

So far some comments are there, mostly about the cometic & structure
of code changes.

On 9/25/25 21:35, Cristian Marussi wrote:
> Add basic support for SCMI V4.0-alpha_0 Telemetry protocol including SHMTI,
> FastChannels, Notifications and Single Sample Reads collection methods.

Could it be possible to split this big patch to a few with those
mentioned features? It would also be easier to review (at least from
my perspective). If it too complex and interdependent than OK, I will
just continue.

> 
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
>   drivers/firmware/arm_scmi/Makefile    |    2 +-
>   drivers/firmware/arm_scmi/driver.c    |    2 +
>   drivers/firmware/arm_scmi/protocols.h |    1 +
>   drivers/firmware/arm_scmi/telemetry.c | 2117 +++++++++++++++++++++++++
>   include/linux/scmi_protocol.h         |  185 ++-
>   5 files changed, 2305 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/firmware/arm_scmi/telemetry.c
> 
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 780cd62b2f78..fe55b7aa0707 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -8,7 +8,7 @@ scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
>   scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
>   scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
>   scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
> -scmi-protocols-y += pinctrl.o
> +scmi-protocols-y += pinctrl.o telemetry.o
>   scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
>   
>   obj-$(CONFIG_ARM_SCMI_PROTOCOL) += transports/
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 8f969d8b86a6..801d59e6b3bc 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -3468,6 +3468,7 @@ static int __init scmi_driver_init(void)
>   	scmi_system_register();
>   	scmi_powercap_register();
>   	scmi_pinctrl_register();
> +	scmi_telemetry_register();
>   
>   	return platform_driver_register(&scmi_driver);
>   }
> @@ -3486,6 +3487,7 @@ static void __exit scmi_driver_exit(void)
>   	scmi_system_unregister();
>   	scmi_powercap_unregister();
>   	scmi_pinctrl_unregister();
> +	scmi_telemetry_unregister();
>   
>   	platform_driver_unregister(&scmi_driver);
>   
> diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
> index 2e40a7bb5b01..edd83a02e272 100644
> --- a/drivers/firmware/arm_scmi/protocols.h
> +++ b/drivers/firmware/arm_scmi/protocols.h
> @@ -387,5 +387,6 @@ DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>   DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
>   DECLARE_SCMI_REGISTER_UNREGISTER(system);
>   DECLARE_SCMI_REGISTER_UNREGISTER(powercap);
> +DECLARE_SCMI_REGISTER_UNREGISTER(telemetry);
>   
>   #endif /* _SCMI_PROTOCOLS_H */
> diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_scmi/telemetry.c
> new file mode 100644
> index 000000000000..f03000c173c2
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/telemetry.c
> @@ -0,0 +1,2117 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * System Control and Management Interface (SCMI) Telemetry Protocol
> + *
> + * Copyright (C) 2025 ARM Ltd.
> + *
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/limits.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/refcount.h>
> +#include <linux/slab.h>
> +#include <linux/sprintf.h>
> +#include <linux/string.h>
> +#include <linux/xarray.h>
> +
> +#include "protocols.h"
> +#include "notify.h"
> +
> +/* Updated only after ALL the mandatory features for that version are merged */
> +#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
> +
> +#define SCMI_TLM_TDCF_MAX_RETRIES	5
> +
> +enum scmi_telemetry_protocol_cmd {
> +	TELEMETRY_LIST_SHMTI = 0x3,
> +	TELEMETRY_DE_DESCRIPTION = 0x4,
> +	TELEMETRY_LIST_UPDATE_INTERVALS = 0x5,
> +	TELEMETRY_DE_CONFIGURE = 0x6,
> +	TELEMETRY_DE_ENABLED_LIST = 0x7, //TODO IMPLEMENT
> +	TELEMETRY_CONFIG_SET = 0x8,
> +	TELEMETRY_READING_COMPLETE = TELEMETRY_CONFIG_SET,
> +	TELEMETRY_CONFIG_GET = 0x9, //TODO IMPLEMENT !
> +	TELEMETRY_RESET = 0xA,
> +};
> +
> +struct scmi_msg_resp_telemetry_protocol_attributes {
> +	__le32 de_num;
> +	__le32 groups_num;
> +	__le32 de_implementation_rev_dword[SCMI_TLM_DE_IMPL_MAX_DWORDS];
> +	__le32 attributes;
> +#define SUPPORTS_SINGLE_READ(x)		((x) & BIT(31))
> +#define SUPPORTS_CONTINUOS_UPDATE(x)	((x) & BIT(30))
> +#define SUPPORTS_PER_GROUP_CONFIG(x)	((x) & BIT(18))
> +#define SUPPORTS_RESET(x)		((x) & BIT(17))
> +#define SUPPORTS_FC(x)			((x) & BIT(16))

I would move those defines into a dedicated section of
'all defines' which will have a comment for each such group.


> +};
> +
> +struct scmi_telemetry_update_notify_payld {
> +	__le32 agent_id;
> +	__le32 status;
> +	__le32 num_dwords;
> +	__le32 array[];
> +};
> +
> +struct scmi_shmti_desc {
> +	__le32 id;
> +	__le32 addr_low;
> +	__le32 addr_high;
> +	__le32 length;
> +};
> +
> +struct scmi_msg_resp_telemetry_shmti_list {
> +	__le32 num_shmti;
> +	struct scmi_shmti_desc desc[];
> +};
> +
> +struct de_desc_fc {
> +	__le32 addr_low;
> +	__le32 addr_high;
> +	__le32 size;
> +};
> +
> +struct scmi_de_desc {
> +	__le32 id;
> +	__le32 grp_id;
> +	__le32 data_sz;
> +	__le32 attr_1;
> +#define	IS_NAME_SUPPORTED(d)	((d)->attr_1 & BIT(31))
> +#define	IS_FC_SUPPORTED(d)	((d)->attr_1 & BIT(30))
> +#define	GET_DE_TYPE(d)		(le32_get_bits((d)->attr_1, GENMASK(29, 22)))
> +#define	IS_PERSISTENT(d)	((d)->attr_1 & BIT(21))
> +#define GET_DE_UNIT_EXP(d)						\
> +	({								\
> +		int __signed_exp =					\
> +			le32_get_bits((d)->attr_1, GENMASK(20, 13));	\
> +									\
> +		if (__signed_exp & BIT(7))				\
> +			__signed_exp |= GENMASK(31, 8);			\
> +		__signed_exp;						\
> +	})
> +#define	GET_DE_UNIT(d)		(le32_get_bits((d)->attr_1, GENMASK(12, 5)))
> +
> +#define GET_DE_TSTAMP_EXP(d)						\
> +	({								\
> +		int __signed_exp =					\
> +			FIELD_GET(GENMASK(4, 1), (d)->attr_1);		\
> +									\
> +		if (__signed_exp & BIT(3))				\
> +			__signed_exp |= GENMASK(31, 4);			\
> +		__signed_exp;						\
> +	})
> +#define	IS_TSTAMP_SUPPORTED(d)	((d)->attr_1 & BIT(0))
> +	__le32 attr_2;
> +#define	GET_DE_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(31, 24)))
> +#define	GET_COMPO_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(23, 8)))
> +#define	GET_COMPO_TYPE(d)	(le32_get_bits((d)->attr_2, GENMASK(7, 0)))


Same here. Adding them inside those structures causes somehow
complex to read and undertand block of code.
If you could move it into seperate 'macros' section, would that
casue you some issue?


> +	__le32 reserved;
> +};
> +
> +struct scmi_msg_resp_telemetry_de_description {
> +	__le32 num_desc;
> +	struct scmi_de_desc desc[];
> +};
> +
> +struct scmi_msg_telemetry_update_intervals {
> +	__le32 index;
> +	__le32 group_identifier;
> +#define	ALL_DES_NO_GROUP	0x0
> +#define SPECIFIC_GROUP_DES	0x1
> +#define ALL_DES_ANY_GROUP	0x2
> +	__le32 flags;
> +};
> +
> +struct scmi_msg_resp_telemetry_update_intervals {
> +	__le32 flags;
> +#define INTERVALS_DISCRETE(x)	(!((x) & BIT(12)))
> +	__le32 intervals[];
> +};
> +
> +struct scmi_msg_telemetry_de_configure {
> +	__le32 id;
> +	__le32 flags;
> +#define DE_ENABLE_NO_TSTAMP	BIT(0)
> +#define DE_ENABLE_WTH_TSTAMP	BIT(1)
> +#define DE_DISABLE_ALL		BIT(2)
> +#define GROUP_SELECTOR		BIT(3)
> +#define EVENT_DE		0
> +#define EVENT_GROUP		1
> +#define DE_DISABLE_ONE		0x0
> +};
> +
> +struct scmi_msg_resp_telemetry_de_configure {
> +	__le32 shmti_id;
> +#define IS_SHMTI_ID_VALID(x)	((x) != 0xFFFFFFFF)
> +	__le32 tdcf_de_offset;
> +};
> +
> +struct scmi_msg_telemetry_config_set {
> +	__le32 grp_id;
> +	__le32 control;
> +#define TELEMETRY_ENABLE		(BIT(0))
> +
> +#define TELEMETRY_MODE(x)		(FIELD_PREP(GENMASK(4, 1), (x)))
> +#define TELEMETRY_MODE_ONDEMAND		TELEMETRY_MODE(0)
> +#define TELEMETRY_MODE_NOTIFS		TELEMETRY_MODE(1)
> +#define TELEMETRY_MODE_SINGLE		TELEMETRY_MODE(2)
> +
> +#define TELEMETRY_SELECTOR(x)		(FIELD_PREP(GENMASK(8, 5), (x)))
> +#define	TELEMETRY_SELECTOR_ORPHANS	TELEMETRY_SELECTOR(0)
> +#define	TELEMETRY_SELECTOR_GROUP	TELEMETRY_SELECTOR(1)
> +#define	TELEMETRY_SELECTOR_ALL		TELEMETRY_SELECTOR(2)
> +	__le32 sampling_rate;
> +};
> +
> +struct scmi_msg_resp_telemetry_reading_complete {
> +	__le32 num_dwords;
> +	__le32 dwords[];
> +};
> +
> +/* TDCF */
> +
> +#define TO_CPU_64(h, l)	(((u64)le32_to_cpu((h)) << 32) | le32_to_cpu((l)))
> +
> +struct fc_line {
> +	u32 data_low;
> +	u32 data_high;
> +};
> +
> +struct fc_tsline {
> +	u32 data_low;
> +	u32 data_high;
> +	u32 ts_low;
> +	u32 ts_high;
> +};
> +
> +struct line {
> +	u32 data_low;
> +	u32 data_high;
> +};
> +
> +struct blk_tsline {
> +	u32 ts_low;
> +	u32 ts_high;
> +};
> +
> +struct tsline {
> +	u32 data_low;
> +	u32 data_high;
> +	u32 ts_low;
> +	u32 ts_high;
> +};
> +
> +#define LINE_DATA_GET(f)				\
> +({							\
> +	typeof(f) _f = (f);				\
> +							\
> +	(TO_CPU_64((_f)->data_high, (_f)->data_low));	\
> +})
> +
> +#define LINE_TSTAMP_GET(f)				\
> +({							\
> +	typeof(f) _f = (f);				\
> +							\
> +	(TO_CPU_64((_f)->ts_high, (_f)->ts_low));	\
> +})
> +
> +#define BLK_TSTAMP_GET(f)	LINE_TSTAMP_GET(f)

These defines also can go into the 'all macros' section.

> +
> +struct payload {
> +	u32 meta;
> +#define IS_BLK_TS(x)	((x)->meta & BIT(4))
> +#define USE_BLK_TS(x)	((x)->meta & BIT(3))
> +#define USE_LINE_TS(x)	((x)->meta & BIT(2))
> +#define TS_VALID(x)	((x)->meta & BIT(1))
> +#define	DATA_INVALID(x) ((x)->meta & BIT(0))
> +	u32 id;
> +	union {
> +		struct line l;
> +		struct tsline tsl;
> +		struct blk_tsline blk_tsl;
> +	};
> +};
> +
> +#define PAYLD_ID(x)	(le32_to_cpu(((struct payload *)(x))->id))
> +
> +#define LINE_DATA_PAYLD_WORDS						       \
> +	((sizeof(u32) + sizeof(u32) + sizeof(struct line)) / sizeof(u32))
> +#define TS_LINE_DATA_PAYLD_WORDS					       \
> +	((sizeof(u32) + sizeof(u32) + sizeof(struct tsline)) / sizeof(u32))
> +
> +#define QWORDS_LINE_DATA_PAYLD		(LINE_DATA_PAYLD_WORDS / 2)
> +#define QWORDS_TS_LINE_DATA_PAYLD	(TS_LINE_DATA_PAYLD_WORDS / 2)
> +
> +struct prlg {
> +	u32 seq_low;
> +	u32 seq_high;
> +	u32 num_qwords;
> +	u32 _meta_header_high;
> +};
> +
> +struct eplg {
> +	u32 seq_low;
> +	u32 seq_high;
> +};
> +
> +#define TDCF_EPLG_SZ	(sizeof(struct eplg))
> +
> +struct tdcf {
> +	struct prlg prlg;
> +	unsigned char payld[];
> +};
> +
> +#define SHMTI_MIN_SIZE	(sizeof(struct tdcf) + TDCF_EPLG_SZ)
> +
> +#define TDCF_START_SEQ_GET(x)					\
> +	({							\
> +		u64 _val;					\
> +		struct prlg *_p = &((x)->prlg);			\
> +								\
> +		_val = TO_CPU_64(_p->seq_high, _p->seq_low);	\
> +		(_val);						\
> +	})
> +
> +#define IS_BAD_START_SEQ(s)	((s) & 0x1)
> +
> +#define	TDCF_END_SEQ_GET(e)					\
> +	({							\
> +		u64 _val;					\
> +		struct eplg *_e = (e);				\
> +								\
> +		_val = TO_CPU_64(_e->seq_high, _e->seq_low);	\
> +		(_val);						\
> +	 })
> +
> +struct telemetry_shmti {
> +	int id;
> +	void __iomem *base;
> +	u32 len;
> +	u64 last_magic;
> +};
> +
> +#define SHMTI_EPLG(s)						\
> +	({							\
> +		struct telemetry_shmti *_s = (s);		\
> +		void *_eplg;					\
> +								\
> +		_eplg = _s->base + _s->len - TDCF_EPLG_SZ;	\
> +		(_eplg);					\
> +	})
> +
> +struct telemetry_info {
> +	bool streaming_mode;
> +	int num_shmti;
> +	struct device *dev;
> +	struct telemetry_shmti *shmti;
> +	struct xarray xa_des;
> +	struct xarray xa_bts;
> +	struct scmi_telemetry_info info;
> +	struct notifier_block telemetry_nb;
> +};
> +
> +#define telemetry_nb_to_info(x)	\
> +	container_of(x, struct telemetry_info, telemetry_nb)
> +
> +struct telemetry_block_ts {
> +	refcount_t users;
> +	/* Protect block_ts accesses  */
> +	struct mutex mtx;
> +	u64 last_ts;
> +	u64 last_magic;
> +	struct payload __iomem *payld;
> +	struct xarray *xa_bts;
> +};
> +
> +struct telemetry_de {
> +	bool cached;
> +	void __iomem *base;
> +	void __iomem *eplg;
> +	u32 offset;
> +	/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */
> +	u32 fc_size;
> +	/* Protect last_val/ts/magic accesses  */
> +	struct mutex mtx;
> +	u64 last_val;
> +	u64 last_ts;
> +	u64 last_magic;
> +	struct telemetry_block_ts *bts;
> +	struct scmi_telemetry_de de;
> +};
> +
> +#define to_tde(d)	container_of(d, struct telemetry_de, de)
> +
> +struct scmi_tlm_de_priv {
> +	struct telemetry_info *ti;
> +	void *next;
> +};
> +
> +static int
> +scmi_telemetry_protocol_attributes_get(const struct scmi_protocol_handle *ph,
> +				       struct telemetry_info *ti)
> +{
> +	int ret;
> +	struct scmi_xfer *t;
> +	struct scmi_msg_resp_telemetry_protocol_attributes *resp;
> +
> +	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
> +				      0, sizeof(*resp), &t);
> +	if (ret)
> +		return ret;
> +
> +	resp = t->rx.buf;
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret) {
> +		__le32 attr = resp->attributes;
> +
> +		ti->info.base.num_des = le32_to_cpu(resp->de_num);
> +		ti->info.base.num_groups = le32_to_cpu(resp->groups_num);
> +		for (int i = 0; i < SCMI_TLM_DE_IMPL_MAX_DWORDS; i++)
> +			ti->info.base.de_impl_version[i] =
> +				le32_to_cpu(resp->de_implementation_rev_dword[i]);
> +		ti->info.single_read_support = SUPPORTS_SINGLE_READ(attr);
> +		ti->info.continuos_update_support = SUPPORTS_CONTINUOS_UPDATE(attr);
> +		ti->info.per_group_config_support = SUPPORTS_PER_GROUP_CONFIG(attr);
> +		ti->info.reset_support = SUPPORTS_RESET(attr);
> +		ti->info.fc_support = SUPPORTS_FC(attr);
> +		ti->num_shmti = le32_get_bits(attr, GENMASK(15, 0));
> +		/* Allocate DEs descriptors */
> +		ti->info.des = devm_kcalloc(ph->dev, ti->info.base.num_des,
> +					    sizeof(*ti->info.des), GFP_KERNEL);
> +		if (!ti->info.des) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +
> +		/* Allocate a set of contiguous DE info descriptors. */
> +		ti->info.des_store = devm_kcalloc(ph->dev, ti->info.base.num_des,
> +						  sizeof(*ti->info.des_store),
> +						  GFP_KERNEL);
> +		if (!ti->info.des_store) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +
> +		/* Allocate DE GROUPS descriptors */
> +		ti->info.groups = devm_kcalloc(ph->dev, ti->info.base.num_groups,
> +					       sizeof(*ti->info.groups), GFP_KERNEL);
> +		if (!ti->info.groups) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +
> +		/* Allocate a set of contiguous Group info descriptors. */
> +		ti->info.grps_store = devm_kcalloc(ph->dev, ti->info.base.num_groups,
> +						   sizeof(*ti->info.grps_store),
> +						   GFP_KERNEL);
> +		if (!ti->info.grps_store) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +
> +		for (int i = 0; i < ti->info.base.num_groups; i++) {
> +			ti->info.grps_store[i].id = i;
> +			/* Bind contiguous Group info struct */
> +			ti->info.groups[i].info = &ti->info.grps_store[i];
> +		}
> +	}

Could this be changed to:

	if (ret)
		goto out;

then the code would be more on the left side not in the if block.

> +
> +out:
> +	ph->xops->xfer_put(ph, t);
> +
> +	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));
> +
> +	/* Initialized to first descriptor */
> +	p->next = (void *)r->desc;
> +
> +	return 0;
> +}
> +
> +static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,

Maybe break the line after 'int' and gain a bit more space in the new
line?

> +					  const void *response,
> +					  struct scmi_iterator_state *st,
> +					  void *priv)
> +{
> +	struct telemetry_de *tde;
> +	struct scmi_tlm_de_priv *p = priv;
> +	const struct scmi_de_desc *desc = p->next;
> +	unsigned int grp_id;
> +	int ret;
> +
> +	tde = to_tde(p->ti->info.des[st->desc_index + st->loop_idx]);
> +
> +	tde->de.info->id = le32_to_cpu(desc->id);
> +	grp_id = le32_to_cpu(desc->grp_id);
> +	if (grp_id != SCMI_TLM_GRP_INVALID) {
> +		/* Group descriptors are empty but allocated at this point */
> +		if (grp_id >= p->ti->info.base.num_groups)
> +			return -EINVAL;
> +
> +		/* Link to parent group */
> +		tde->de.info->grp_id = grp_id;
> +		tde->de.grp = &p->ti->info.groups[grp_id];
> +	}
> +	tde->de.info->data_sz = le32_to_cpu(desc->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->tstamp_exp = GET_DE_TSTAMP_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->de.tstamp_support = IS_TSTAMP_SUPPORTED(desc);
> +	tde->de.fc_support = IS_FC_SUPPORTED(desc);
> +	tde->de.name_support = IS_NAME_SUPPORTED(desc);
> +	p->next += sizeof(*desc);
> +	if (tde->de.fc_support) {
> +		u32 size;
> +		u64 phys_addr;
> +		void __iomem *addr;
> +		struct de_desc_fc *dfc;
> +
> +		dfc = p->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(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 */
> +		p->next += sizeof(*dfc);
> +	}
> +
> +	if (tde->de.name_support) {
> +		const char *de_name = p->next;
> +
> +		strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE);
> +		//tde->de.name = tde->name;
> +
> +		/* Variably sized depending on name support */
> +		p->next += SCMI_SHORT_NAME_MAX_SIZE;
> +	}
> +
> +	/* Store DE pointer by de_id */
> +	ret = xa_insert(&p->ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL);
> +	if (ret)
> +		return ret;
> +
> +	/* 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)
> +{
> +	/* Allocate all groups DEs IDs arrays at first ... */
> +	for (int i = 0; i < ti->info.base.num_groups; i++) {
> +		struct scmi_telemetry_group *grp = &ti->info.groups[i];
> +
> +		grp->des = devm_kcalloc(dev, grp->info->num_des,
> +					sizeof(unsigned int), GFP_KERNEL);
> +		if (!grp->des)
> +			return -ENOMEM;
> +
> +		/*
> +		 * Max size 32bit ID string in Hex: 0xCAFECAFE
> +		 *  - 10 digits + ' '/'\n' = 11 bytes per  number
> +		 *  - terminating NUL character
> +		 */
> +		grp->des_str_sz = grp->info->num_des * 11 + 1;
> +		grp->des_str = devm_kzalloc(dev, grp->des_str_sz, GFP_KERNEL);
> +		if (!grp->des_str)
> +			return -ENOMEM;
> +
> +		/* Reset group DE counter */
> +		grp->info->num_des = 0;
> +	}
> +
> +	/* Scan DEs and populate DE IDs arrays for all groups */
> +	for (int i = 0; i < ti->info.base.num_des; i++) {
> +		struct scmi_telemetry_group *grp = ti->info.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 compsing DES string */
> +	for (int i = 0; i < ti->info.base.num_groups; i++) {
> +		struct scmi_telemetry_group *grp = &ti->info.groups[i];
> +		char *buf = grp->des_str;
> +		size_t bufsize = grp->des_str_sz;
> +
> +		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%04X%c",
> +					ti->info.des[grp->des[j]]->info->id, term);
> +
> +			buf += len;
> +			bufsize -= len;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +scmi_telemetry_de_descriptors_get(const struct scmi_protocol_handle *ph,
> +				  struct telemetry_info *ti)
> +{
> +	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;
> +
> +	xa_init(&ti->xa_des);
> +	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 scmi_telemetry_de_groups_init(ph->dev, ti);
> +}
> +
> +static int scmi_telemetry_enumerate_de(const struct scmi_protocol_handle *ph,
> +				       struct telemetry_info *ti)
> +{
> +	int ret;
> +
> +	if (!ti->info.base.num_des)
> +		return 0;
> +
> +	for (int i = 0; i < ti->info.base.num_des; i++) {
> +		struct telemetry_de *tde;
> +
> +		tde = devm_kzalloc(ph->dev, sizeof(*tde), GFP_KERNEL);
> +		if (!tde)
> +			return -ENOMEM;
> +
> +		mutex_init(&tde->mtx);
> +
> +		/* Bind contiguous DE info structures */
> +		tde->de.info = &ti->info.des_store[i];
> +		ti->info.des[i] = &tde->de;
> +	}
> +
> +	ret = scmi_telemetry_de_descriptors_get(ph, ti);
> +	if (ret) {
> +		dev_err(ph->dev, "Cannot get DE descriptors");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +struct scmi_tlm_ivl_priv {
> +	struct device *dev;
> +	struct scmi_tlm_intervals **intrvs;
> +	unsigned int grp_id;
> +	unsigned int flags;
> +};

I would also move all such structures into top of the file,
into a dedicated section.

> +
> +static void iter_intervals_prepare_message(void *message,
> +					   unsigned int desc_index,
> +					   const void *priv)
> +{
> +	struct scmi_msg_telemetry_update_intervals *msg = message;
> +	const struct scmi_tlm_ivl_priv *p = priv;
> +
> +	msg->index = cpu_to_le32(desc_index);
> +	msg->group_identifier = cpu_to_le32(p->grp_id);
> +	msg->flags = FIELD_PREP(GENMASK(3, 0), p->flags);
> +}
> +
> +static int iter_intervals_update_state(struct scmi_iterator_state *st,
> +				       const void *response, void *priv)
> +{
> +	const struct scmi_msg_resp_telemetry_update_intervals *r = response;
> +
> +	st->num_returned = le32_get_bits(r->flags, GENMASK(11, 0));
> +	st->num_remaining = le32_get_bits(r->flags, GENMASK(31, 16));
> +
> +	/*
> +	 * total intervals is not declared previously anywhere so we
> +	 * assume it's returned+remaining on first call.
> +	 */
> +	if (!st->max_resources) {
> +		struct scmi_tlm_ivl_priv *p = priv;
> +		struct scmi_tlm_intervals *intrvs;
> +		int inum;
> +
> +		inum = st->num_returned + st->num_remaining;
> +		intrvs = devm_kzalloc(p->dev,
> +				      sizeof(*intrvs) + inum * sizeof(__u32),
> +				      GFP_KERNEL);
> +		if (!intrvs)
> +			return -ENOMEM;
> +
> +		intrvs->discrete = INTERVALS_DISCRETE(r->flags);
> +		/* Check consistency on first call */
> +		if (!intrvs->discrete &&
> +		    (st->num_returned != 3 || st->num_remaining != 0))
> +			return -EINVAL;
> +
> +		intrvs->num = inum;
> +		st->max_resources = intrvs->num;
> +
> +		*p->intrvs = intrvs;
> +	}

maybe:
	if (st->max_resources)
		goto out;

	.....

out:
	return 0;


> +
> +	return 0;
> +}
> +
> +static int
> +iter_intervals_process_response(const struct scmi_protocol_handle *ph,
> +				const void *response,
> +				struct scmi_iterator_state *st, void *priv)
> +{
> +	const struct scmi_msg_resp_telemetry_update_intervals *r = response;
> +	struct scmi_tlm_ivl_priv *p = priv;
> +	struct scmi_tlm_intervals *intrvs = *p->intrvs;
> +	unsigned int idx = st->loop_idx;
> +
> +	intrvs->update_intervals[st->desc_index + idx] = r->intervals[idx];
> +
> +	return 0;
> +}
> +
> +static int
> +scmi_tlm_enumerate_update_intervals(const struct scmi_protocol_handle *ph,
> +				    struct telemetry_info *ti, int grp_id,
> +				    unsigned int flags)
> +{
> +	struct scmi_iterator_ops ops = {
> +		.prepare_message = iter_intervals_prepare_message,
> +		.update_state = iter_intervals_update_state,
> +		.process_response = iter_intervals_process_response,
> +	};
> +	struct scmi_tlm_ivl_priv ipriv = {
> +		.dev = ph->dev,
> +		.grp_id = grp_id,
> +		.intrvs = (grp_id == SCMI_TLM_GRP_INVALID) ?
> +			&ti->info.intervals :
> +			&ti->info.groups[grp_id].intervals,
> +		.flags = flags,
> +	};
> +	void *iter;
> +
> +	iter = ph->hops->iter_response_init(ph, &ops, 0,
> +					    TELEMETRY_LIST_UPDATE_INTERVALS,
> +			     sizeof(struct scmi_msg_telemetry_update_intervals),
> +					    &ipriv);
> +	if (IS_ERR(iter))
> +		return PTR_ERR(iter);
> +
> +	return ph->hops->iter_response_run(iter);
> +}
> +
> +static int
> +scmi_telemetry_enumerate_update_intervals(const struct scmi_protocol_handle *ph,
> +					  struct telemetry_info *ti)
> +{
> +	int ret;
> +	unsigned int flags;
> +
> +	flags = !ti->info.per_group_config_support ?
> +		ALL_DES_ANY_GROUP : ALL_DES_NO_GROUP;
> +
> +	ret = scmi_tlm_enumerate_update_intervals(ph, ti, SCMI_TLM_GRP_INVALID,
> +						  flags);
> +	if (ret)
> +		return ret;
> +
> +	/* A copy for UAPI access... */
> +	ti->info.base.num_intervals = ti->info.intervals->num;
> +	if (ti->info.base.num_groups && ti->info.per_group_config_support) {
> +		flags = SPECIFIC_GROUP_DES;
> +		for (int id = 0; id < ti->info.base.num_groups; id++) {
> +			ret = scmi_tlm_enumerate_update_intervals(ph, ti, id,
> +								  flags);
> +			if (ret)
> +				break;
> +
> +			ti->info.grps_store[id].num_intervals =
> +				ti->info.groups[id].intervals->num;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int iter_shmti_update_state(struct scmi_iterator_state *st,
> +				   const void *response, void *priv)
> +{
> +	const struct scmi_msg_resp_telemetry_shmti_list *r = response;
> +
> +	st->num_returned = le32_get_bits(r->num_shmti, GENMASK(15, 0));
> +	st->num_remaining = le32_get_bits(r->num_shmti, GENMASK(31, 16));
> +
> +	return 0;
> +}
> +
> +static int iter_shmti_process_response(const struct scmi_protocol_handle *ph,
> +				       const void *response,
> +				       struct scmi_iterator_state *st,
> +				       void *priv)
> +{
> +	const struct scmi_msg_resp_telemetry_shmti_list *r = response;
> +	struct telemetry_info *ti = priv;
> +	struct telemetry_shmti *shmti;
> +	const struct scmi_shmti_desc *desc;
> +	void __iomem *addr;
> +	u64 phys_addr;
> +	u32 len;
> +
> +	desc = &r->desc[st->loop_idx];
> +	shmti = &ti->shmti[st->desc_index + st->loop_idx];
> +
> +	shmti->id = le32_to_cpu(desc->id);
> +	phys_addr = le32_to_cpu(desc->addr_low);
> +	phys_addr |= (u64)le32_to_cpu(desc->addr_high) << 32;
> +
> +	len = le32_to_cpu(desc->length);
> +	if (len < SHMTI_MIN_SIZE)
> +		return -EINVAL;
> +
> +	addr = devm_ioremap(ph->dev, phys_addr, len);
> +	if (!addr)
> +		return -EADDRNOTAVAIL;
> +
> +	shmti->base = addr;
> +	shmti->len = len;
> +
> +	return 0;
> +}
> +
> +static int scmi_telemetry_shmti_list(const struct scmi_protocol_handle *ph,
> +				     struct telemetry_info *ti)
> +{
> +	struct scmi_iterator_ops ops = {
> +		.prepare_message = iter_tlm_prepare_message,
> +		.update_state = iter_shmti_update_state,
> +		.process_response = iter_shmti_process_response,
> +	};
> +	void *iter;
> +
> +	iter = ph->hops->iter_response_init(ph, &ops, ti->info.base.num_des,
> +					    TELEMETRY_LIST_SHMTI,
> +					    sizeof(u32), ti);
> +	if (IS_ERR(iter))
> +		return PTR_ERR(iter);
> +
> +	return ph->hops->iter_response_run(iter);
> +}
> +
> +static int scmi_telemetry_enumerate_shmti(const struct scmi_protocol_handle *ph,
> +					  struct telemetry_info *ti)
> +{
> +	int ret;
> +
> +	if (!ti->num_shmti)
> +		return 0;
> +
> +	ti->shmti = devm_kcalloc(ph->dev, ti->num_shmti, sizeof(*ti->shmti),
> +				 GFP_KERNEL);
> +	if (!ti->shmti)
> +		return -ENOMEM;
> +
> +	ret = scmi_telemetry_shmti_list(ph, ti);
> +	if (ret) {
> +		dev_err(ph->dev, "Cannot get SHMTI list descriptors");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct scmi_telemetry_info *
> +scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)
> +{
> +	struct telemetry_info *ti = ph->get_priv(ph);
> +
> +	return &ti->info;
> +}
> +
> +static const struct scmi_tlm_de_info *
> +scmi_telemetry_de_info_get(const struct scmi_protocol_handle *ph, u32 id)
> +{
> +	struct telemetry_info *ti = ph->get_priv(ph);
> +
> +	return xa_load(&ti->xa_des, id);
> +}
> +
> +static u64
> +scmi_telemetry_blkts_read(u64 magic, struct telemetry_block_ts *bts)
> +{
> +	if (WARN_ON(!bts || !refcount_read(&bts->users)))
> +		return 0;
> +
> +	guard(mutex)(&bts->mtx);
> +
> +	if (bts->last_magic == magic)
> +		return bts->last_ts;
> +
> +	bts->last_ts = BLK_TSTAMP_GET(&bts->payld->blk_tsl);
> +	bts->last_magic = magic;
> +
> +	return bts->last_ts;
> +}
> +
> +static void scmi_telemetry_blkts_update(u64 magic,
> +					struct telemetry_block_ts *bts)
> +{
> +	guard(mutex)(&bts->mtx);
> +
> +	if (bts->last_magic != magic) {
> +		bts->last_ts = BLK_TSTAMP_GET(&bts->payld->blk_tsl);
> +		bts->last_magic = magic;
> +	}
> +}
> +
> +static void scmi_telemetry_blkts_put(struct device *dev,
> +				     struct telemetry_block_ts *bts)
> +{
> +	if (refcount_dec_and_test(&bts->users)) {
> +		scoped_guard(mutex, &bts->mtx)
> +			xa_erase(bts->xa_bts, (unsigned long)bts->payld);
> +		devm_kfree(dev, bts);
> +	}
> +}
> +
> +static struct telemetry_block_ts *
> +scmi_telemetry_blkts_get(struct xarray *xa_bts, struct payload *payld)
> +{
> +	struct telemetry_block_ts *bts;
> +
> +	bts = xa_load(xa_bts, (unsigned long)payld);
> +	if (!bts)
> +		return NULL;
> +
> +	refcount_inc(&bts->users);
> +
> +	return bts;
> +}
> +
> +static struct payload *
> +scmi_telemetry_nearest_blk_ts(struct telemetry_shmti *shmti,
> +			      struct payload *last_payld)
> +{
> +	struct payload *payld, *bts_payld = NULL;
> +	struct tdcf __iomem *tdcf = shmti->base;
> +	u32 *next;
> +
> +	/* Scan from start of TDCF payloads up to last_payld */
> +	payld = (struct payload *)tdcf->payld;
> +	next = (u32 *)payld;
> +	while (payld < last_payld) {
> +		if (IS_BLK_TS(payld))
> +			bts_payld = payld;
> +
> +		next += USE_LINE_TS(payld) ?
> +			TS_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS;
> +		payld = (struct payload *)next;
> +	}
> +
> +	return bts_payld;
> +}
> +
> +static struct telemetry_block_ts *
> +scmi_telemetry_blkts_lookup(struct device *dev, struct xarray *xa_bts,
> +			    struct payload *payld)
> +{
> +	struct telemetry_block_ts *bts;
> +
> +	bts = xa_load(xa_bts, (unsigned long)payld);
> +	if (!bts) {
> +		int ret;
> +
> +		bts = devm_kzalloc(dev, sizeof(*bts), GFP_KERNEL);
> +		if (!bts)
> +			return NULL;
> +
> +		refcount_set(&bts->users, 1);
> +		bts->payld = payld;
> +		bts->xa_bts = xa_bts;
> +		mutex_init(&bts->mtx);
> +		ret = xa_insert(xa_bts, (unsigned long)payld, bts, GFP_KERNEL);
> +		if (ret) {
> +			devm_kfree(dev, bts);
> +			return NULL;
> +		}
> +	}
> +
> +	return bts;
> +}
> +
> +static struct telemetry_block_ts *
> +scmi_telemetry_blkts_bind(struct device *dev, struct telemetry_shmti *shmti,
> +			  struct payload *payld, struct xarray *xa_bts)
> +{
> +	struct telemetry_block_ts *bts;
> +	struct payload *bts_payld;
> +
> +	/* Find the BLK_TS immediately preceding this DE payld */
> +	bts_payld = scmi_telemetry_nearest_blk_ts(shmti, payld);
> +	if (!bts_payld)
> +		return NULL;
> +
> +	bts = scmi_telemetry_blkts_get(xa_bts, bts_payld);
> +	if (bts)
> +		return bts;
> +
> +	return scmi_telemetry_blkts_lookup(dev, xa_bts, payld);
> +}
> +
> +static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti,
> +					    struct payload __iomem *payld,
> +					    struct telemetry_shmti *shmti)
> +{
> +	struct telemetry_block_ts *bts;
> +
> +	/* Check for spec compliance */
> +	if (USE_LINE_TS(payld) || USE_BLK_TS(payld) ||
> +	    DATA_INVALID(payld) || (PAYLD_ID(payld) != 0))
> +		return;
> +
> +	/* A BLK_TS descriptor MUST be returned: it is found or it is crated */
> +	bts = scmi_telemetry_blkts_lookup(ti->dev, &ti->xa_bts, payld);
> +	if (WARN_ON(!bts))
> +		return;
> +
> +	/* Update the descriptor with the lastest TS*/
> +	scmi_telemetry_blkts_update(shmti->last_magic, bts);
> +}
> +
> +static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
> +					   struct payload __iomem *payld,
> +					   struct telemetry_shmti *shmti,
> +					   bool update)
> +{
> +	bool ts_valid = TS_VALID(payld);
> +	struct scmi_telemetry_de *de;
> +	struct telemetry_de *tde;
> +	u64 val, tstamp = 0;
> +	u32 id;
> +
> +	id = PAYLD_ID(payld);
> +	de = xa_load(&ti->xa_des, id);
> +	if (!de)
> +		return;
> +
> +	tde = to_tde(de);
> +	/* Update DE location refs if requested: normally done only on enable */
> +	if (update) {
> +		tde->base = shmti->base;
> +		tde->eplg = SHMTI_EPLG(shmti);
> +		tde->offset = (void *)payld - (void *)shmti->base;
> +	}
> +
> +	scoped_guard(mutex, &tde->mtx) {
> +		if (tde->last_magic == shmti->last_magic)
> +			return;
> +	}
> +
> +	/* Data is always valid since we are NOT handling BLK TS lines here */
> +	val = LINE_DATA_GET(&payld->l);
> +	/* Collect the right TS */
> +	if (ts_valid) {
> +		if (USE_LINE_TS(payld)) {
> +			tstamp = LINE_TSTAMP_GET(&payld->tsl);
> +		} else if (USE_BLK_TS(payld)) {
> +			if (!tde->bts) {
> +				/*
> +				 * Scanning a TDCF looking for the nearest
> +				 * previous valid BLK_TS, after having found a
> +				 * USE_BLK_TS() payload, MUST succeed.
> +				 */
> +				tde->bts = scmi_telemetry_blkts_bind(ti->dev,
> +								     shmti, payld,
> +								     &ti->xa_bts);
> +				if (WARN_ON(!tde->bts))

Do we have to everytime log this when it happens?
It looks like more a debug thing, isn't it?

> +					return;
> +			}
> +
> +			tstamp = scmi_telemetry_blkts_read(tde->last_magic,
> +							   tde->bts);
> +		}
> +	}
> +
> +	guard(mutex)(&tde->mtx);
> +	tde->last_magic = shmti->last_magic;
> +	tde->last_val = val;
> +	if (de->tstamp_enabled)
> +		tde->last_ts = tstamp;
> +	else
> +		tde->last_ts = 0;
> +}
> +
> +static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
> +					  struct payload __iomem *payld,
> +					  struct telemetry_shmti *shmti,
> +					  bool update)
> +{
> +	int used_qwords;
> +
> +	used_qwords = (USE_LINE_TS(payld) && TS_VALID(payld)) ?
> +		QWORDS_TS_LINE_DATA_PAYLD : QWORDS_LINE_DATA_PAYLD;
> +
> +	/*Invalid lines are not an error, could simply be disabled DEs */
> +	if (DATA_INVALID(payld))
> +		return used_qwords;
> +
> +	if (!IS_BLK_TS(payld))
> +		scmi_telemetry_tdcf_data_parse(ti, payld, shmti, update);
> +	else
> +		scmi_telemetry_tdcf_blkts_parse(ti, payld, shmti);
> +
> +	return used_qwords;
> +}
> +
> +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
> +				     unsigned int shmti_id, u64 ts,
> +				     bool update)
> +{
> +	struct telemetry_shmti *shmti = &ti->shmti[shmti_id];
> +	struct tdcf __iomem *tdcf = shmti->base;
> +	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
> +	u64 startm = 0, endm = 0xffffffffffffffff;
> +	void *eplg = SHMTI_EPLG(shmti);
> +
> +	if (!tdcf)
> +		return -ENODEV;
> +
> +	do {
> +		unsigned int qwords;
> +		void __iomem *next;
> +
> +		/* A bit of exponential backoff between retries */
> +		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);

This is something that I would like to understand more.

> +
> +		startm = TDCF_START_SEQ_GET(tdcf);
> +		if (IS_BAD_START_SEQ(startm))
> +			continue;
> +
> +		/* On a BAD_SEQ this will be updated on the next attempt */
> +		shmti->last_magic = startm;
> +
> +		qwords = tdcf->prlg.num_qwords;
> +		next = tdcf->payld;
> +		while (qwords) {
> +			int used_qwords;
> +
> +			used_qwords = scmi_telemetry_tdcf_line_parse(ti, next,
> +								     shmti, update);
> +			if (qwords < used_qwords)
> +				return -EINVAL;
> +
> +			next += used_qwords * 8;
> +			qwords -= used_qwords;
> +		}
> +
> +		endm = TDCF_END_SEQ_GET(eplg);
> +	} while (startm != endm && --retries);
> +
> +	if (startm != endm)
> +		return -EPROTO;
> +
> +	return 0;
> +}
> +

I will take a few steps in this big patch ;)

Regards,
Lukasz

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support
  2025-10-28 11:43   ` Lukasz Luba
@ 2025-10-28 17:51     ` Cristian Marussi
  0 siblings, 0 replies; 32+ messages in thread
From: Cristian Marussi @ 2025-10-28 17:51 UTC (permalink / raw)
  To: Lukasz Luba
  Cc: Cristian Marussi, sudeep.holla, james.quinlan, linux-arm-kernel,
	linux-kernel, f.fainelli, vincent.guittot, etienne.carriere,
	peng.fan, michal.simek, quic_sibis, dan.carpenter, d-gole,
	souvik.chakravarty, arm-scmi

On Tue, Oct 28, 2025 at 11:43:54AM +0000, Lukasz Luba wrote:
> Hi Cristian,
> 

Hi,

> This is a first attempt for the review, because there is so much code.
> I might find something in the next step, because for now I still
> haven't built full picture of it...

Thanks for having a look.

> 
> So far some comments are there, mostly about the cometic & structure
> of code changes.
> 
> On 9/25/25 21:35, Cristian Marussi wrote:
> > Add basic support for SCMI V4.0-alpha_0 Telemetry protocol including SHMTI,
> > FastChannels, Notifications and Single Sample Reads collection methods.
> 
> Could it be possible to split this big patch to a few with those
> mentioned features? It would also be easier to review (at least from
> my perspective). If it too complex and interdependent than OK, I will
> just continue.
> 

Usually I try to keep the protocol layer in one single patch, BUT indeed
the SCMI Telemetry protocol is quite big so I could split the protocol
by collection methods.

> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---

[snip]

> > +struct scmi_msg_resp_telemetry_protocol_attributes {
> > +	__le32 de_num;
> > +	__le32 groups_num;
> > +	__le32 de_implementation_rev_dword[SCMI_TLM_DE_IMPL_MAX_DWORDS];
> > +	__le32 attributes;
> > +#define SUPPORTS_SINGLE_READ(x)		((x) & BIT(31))
> > +#define SUPPORTS_CONTINUOS_UPDATE(x)	((x) & BIT(30))
> > +#define SUPPORTS_PER_GROUP_CONFIG(x)	((x) & BIT(18))
> > +#define SUPPORTS_RESET(x)		((x) & BIT(17))
> > +#define SUPPORTS_FC(x)			((x) & BIT(16))
> 
> I would move those defines into a dedicated section of
> 'all defines' which will have a comment for each such group.
>

Usually all over the SCMI core protocols code we tend to keep all of
such definnes near to the fields of the message descriptors that they
access or manipulate....so for consistency I put this here.

> 
> > +};
> > +
> > +struct scmi_telemetry_update_notify_payld {
> > +	__le32 agent_id;
> > +	__le32 status;
> > +	__le32 num_dwords;
> > +	__le32 array[];
> > +};
> > +
> > +struct scmi_shmti_desc {
> > +	__le32 id;
> > +	__le32 addr_low;
> > +	__le32 addr_high;
> > +	__le32 length;
> > +};
> > +
> > +struct scmi_msg_resp_telemetry_shmti_list {
> > +	__le32 num_shmti;
> > +	struct scmi_shmti_desc desc[];
> > +};
> > +
> > +struct de_desc_fc {
> > +	__le32 addr_low;
> > +	__le32 addr_high;
> > +	__le32 size;
> > +};
> > +
> > +struct scmi_de_desc {
> > +	__le32 id;
> > +	__le32 grp_id;
> > +	__le32 data_sz;
> > +	__le32 attr_1;
> > +#define	IS_NAME_SUPPORTED(d)	((d)->attr_1 & BIT(31))
> > +#define	IS_FC_SUPPORTED(d)	((d)->attr_1 & BIT(30))
> > +#define	GET_DE_TYPE(d)		(le32_get_bits((d)->attr_1, GENMASK(29, 22)))
> > +#define	IS_PERSISTENT(d)	((d)->attr_1 & BIT(21))
> > +#define GET_DE_UNIT_EXP(d)						\
> > +	({								\
> > +		int __signed_exp =					\
> > +			le32_get_bits((d)->attr_1, GENMASK(20, 13));	\
> > +									\
> > +		if (__signed_exp & BIT(7))				\
> > +			__signed_exp |= GENMASK(31, 8);			\
> > +		__signed_exp;						\
> > +	})
> > +#define	GET_DE_UNIT(d)		(le32_get_bits((d)->attr_1, GENMASK(12, 5)))
> > +
> > +#define GET_DE_TSTAMP_EXP(d)						\
> > +	({								\
> > +		int __signed_exp =					\
> > +			FIELD_GET(GENMASK(4, 1), (d)->attr_1);		\
> > +									\
> > +		if (__signed_exp & BIT(3))				\
> > +			__signed_exp |= GENMASK(31, 4);			\
> > +		__signed_exp;						\
> > +	})
> > +#define	IS_TSTAMP_SUPPORTED(d)	((d)->attr_1 & BIT(0))
> > +	__le32 attr_2;
> > +#define	GET_DE_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(31, 24)))
> > +#define	GET_COMPO_INSTA_ID(d)	(le32_get_bits((d)->attr_2, GENMASK(23, 8)))
> > +#define	GET_COMPO_TYPE(d)	(le32_get_bits((d)->attr_2, GENMASK(7, 0)))
> 
> 
> Same here. Adding them inside those structures causes somehow
> complex to read and undertand block of code.
> If you could move it into seperate 'macros' section, would that
> casue you some issue?

No issues...same reason noted above...all protoocls are done like this
(mostly) since the beginning...so I kept the same style.

> 
> 
> > +	__le32 reserved;
> > +};
> > +
> > +struct scmi_msg_resp_telemetry_de_description {
> > +	__le32 num_desc;
> > +	struct scmi_de_desc desc[];
> > +};

[snip]

> > +/* TDCF */
> > +
> > +#define TO_CPU_64(h, l)	(((u64)le32_to_cpu((h)) << 32) | le32_to_cpu((l)))
> > +
> > +struct fc_line {
> > +	u32 data_low;
> > +	u32 data_high;
> > +};
> > +
> > +struct fc_tsline {
> > +	u32 data_low;
> > +	u32 data_high;
> > +	u32 ts_low;
> > +	u32 ts_high;
> > +};
> > +
> > +struct line {
> > +	u32 data_low;
> > +	u32 data_high;
> > +};
> > +
> > +struct blk_tsline {
> > +	u32 ts_low;
> > +	u32 ts_high;
> > +};
> > +
> > +struct tsline {
> > +	u32 data_low;
> > +	u32 data_high;
> > +	u32 ts_low;
> > +	u32 ts_high;
> > +};
> > +
> > +#define LINE_DATA_GET(f)				\
> > +({							\
> > +	typeof(f) _f = (f);				\
> > +							\
> > +	(TO_CPU_64((_f)->data_high, (_f)->data_low));	\
> > +})
> > +
> > +#define LINE_TSTAMP_GET(f)				\
> > +({							\
> > +	typeof(f) _f = (f);				\
> > +							\
> > +	(TO_CPU_64((_f)->ts_high, (_f)->ts_low));	\
> > +})
> > +
> > +#define BLK_TSTAMP_GET(f)	LINE_TSTAMP_GET(f)
> 
> These defines also can go into the 'all macros' section.
> 

Yes these defines indeed are NOT directly related to a message
structure BUT they are related to the above preceding TDCF line
structure definitions...

> > +
> > +struct payload {
> > +	u32 meta;
> > +#define IS_BLK_TS(x)	((x)->meta & BIT(4))
> > +#define USE_BLK_TS(x)	((x)->meta & BIT(3))
> > +#define USE_LINE_TS(x)	((x)->meta & BIT(2))
> > +#define TS_VALID(x)	((x)->meta & BIT(1))
> > +#define	DATA_INVALID(x) ((x)->meta & BIT(0))

[snip]

> > +	resp = t->rx.buf;
> > +	ret = ph->xops->do_xfer(ph, t);
> > +	if (!ret) {
> > +		__le32 attr = resp->attributes;
> > +
> > +		ti->info.base.num_des = le32_to_cpu(resp->de_num);
> > +		ti->info.base.num_groups = le32_to_cpu(resp->groups_num);
> > +		for (int i = 0; i < SCMI_TLM_DE_IMPL_MAX_DWORDS; i++)
> > +			ti->info.base.de_impl_version[i] =
> > +				le32_to_cpu(resp->de_implementation_rev_dword[i]);
> > +		ti->info.single_read_support = SUPPORTS_SINGLE_READ(attr);
> > +		ti->info.continuos_update_support = SUPPORTS_CONTINUOS_UPDATE(attr);
> > +		ti->info.per_group_config_support = SUPPORTS_PER_GROUP_CONFIG(attr);
> > +		ti->info.reset_support = SUPPORTS_RESET(attr);
> > +		ti->info.fc_support = SUPPORTS_FC(attr);
> > +		ti->num_shmti = le32_get_bits(attr, GENMASK(15, 0));
> > +		/* Allocate DEs descriptors */
> > +		ti->info.des = devm_kcalloc(ph->dev, ti->info.base.num_des,
> > +					    sizeof(*ti->info.des), GFP_KERNEL);
> > +		if (!ti->info.des) {
> > +			ret = -ENOMEM;
> > +			goto out;
> > +		}
> > +
> > +		/* Allocate a set of contiguous DE info descriptors. */
> > +		ti->info.des_store = devm_kcalloc(ph->dev, ti->info.base.num_des,
> > +						  sizeof(*ti->info.des_store),
> > +						  GFP_KERNEL);
> > +		if (!ti->info.des_store) {
> > +			ret = -ENOMEM;
> > +			goto out;
> > +		}
> > +
> > +		/* Allocate DE GROUPS descriptors */
> > +		ti->info.groups = devm_kcalloc(ph->dev, ti->info.base.num_groups,
> > +					       sizeof(*ti->info.groups), GFP_KERNEL);
> > +		if (!ti->info.groups) {
> > +			ret = -ENOMEM;
> > +			goto out;
> > +		}
> > +
> > +		/* Allocate a set of contiguous Group info descriptors. */
> > +		ti->info.grps_store = devm_kcalloc(ph->dev, ti->info.base.num_groups,
> > +						   sizeof(*ti->info.grps_store),
> > +						   GFP_KERNEL);
> > +		if (!ti->info.grps_store) {
> > +			ret = -ENOMEM;
> > +			goto out;
> > +		}
> > +
> > +		for (int i = 0; i < ti->info.base.num_groups; i++) {
> > +			ti->info.grps_store[i].id = i;
> > +			/* Bind contiguous Group info struct */
> > +			ti->info.groups[i].info = &ti->info.grps_store[i];
> > +		}
> > +	}
> 
> Could this be changed to:
> 
> 	if (ret)
> 		goto out;
> 
> then the code would be more on the left side not in the if block.

Yes indeed, the classic kernel flow would be like that, but again I was
trying to be consistent with all the other SCMI protocols where usually we
have this kind of construct

	ret = ph->xops->do_xfer(ph, t);
	if (!ret) {
		...PROCESS REPLY ON SUCCESS
	}
	// RELEASE the message xfer anyway
	ph->xops->xfer_put(ph, t);

> > +
> > +out:
> > +	ph->xops->xfer_put(ph, t);
> > +
> > +	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));
> > +
> > +	/* Initialized to first descriptor */
> > +	p->next = (void *)r->desc;
> > +
> > +	return 0;
> > +}
> > +
> > +static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,
> 
> Maybe break the line after 'int' and gain a bit more space in the new
> line?

I will.

> 
> > +					  const void *response,
> > +					  struct scmi_iterator_state *st,
> > +					  void *priv)
> > +{
> > +	struct telemetry_de *tde;
> > +	struct scmi_tlm_de_priv *p = priv;
> > +	const struct scmi_de_desc *desc = p->next;
> > +	unsigned int grp_id;
> > +	int ret;
> > +
> > +	tde = to_tde(p->ti->info.des[st->desc_index + st->loop_idx]);
> > +
> > +	tde->de.info->id = le32_to_cpu(desc->id);
> > +	grp_id = le32_to_cpu(desc->grp_id);
> > +	if (grp_id != SCMI_TLM_GRP_INVALID) {
> > +		/* Group descriptors are empty but allocated at this point */
> > +		if (grp_id >= p->ti->info.base.num_groups)
> > +			return -EINVAL;
> > +
> > +		/* Link to parent group */
> > +		tde->de.info->grp_id = grp_id;
> > +		tde->de.grp = &p->ti->info.groups[grp_id];
> > +	}
> > +	tde->de.info->data_sz = le32_to_cpu(desc->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->tstamp_exp = GET_DE_TSTAMP_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->de.tstamp_support = IS_TSTAMP_SUPPORTED(desc);
> > +	tde->de.fc_support = IS_FC_SUPPORTED(desc);
> > +	tde->de.name_support = IS_NAME_SUPPORTED(desc);
> > +	p->next += sizeof(*desc);
> > +	if (tde->de.fc_support) {
> > +		u32 size;
> > +		u64 phys_addr;
> > +		void __iomem *addr;
> > +		struct de_desc_fc *dfc;
> > +
> > +		dfc = p->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(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 */
> > +		p->next += sizeof(*dfc);
> > +	}
> > +
> > +	if (tde->de.name_support) {
> > +		const char *de_name = p->next;
> > +
> > +		strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE);
> > +		//tde->de.name = tde->name;
> > +
> > +		/* Variably sized depending on name support */
> > +		p->next += SCMI_SHORT_NAME_MAX_SIZE;
> > +	}
> > +
> > +	/* Store DE pointer by de_id */
> > +	ret = xa_insert(&p->ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* 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)
> > +{
> > +	/* Allocate all groups DEs IDs arrays at first ... */
> > +	for (int i = 0; i < ti->info.base.num_groups; i++) {
> > +		struct scmi_telemetry_group *grp = &ti->info.groups[i];
> > +
> > +		grp->des = devm_kcalloc(dev, grp->info->num_des,
> > +					sizeof(unsigned int), GFP_KERNEL);
> > +		if (!grp->des)
> > +			return -ENOMEM;
> > +
> > +		/*
> > +		 * Max size 32bit ID string in Hex: 0xCAFECAFE
> > +		 *  - 10 digits + ' '/'\n' = 11 bytes per  number
> > +		 *  - terminating NUL character
> > +		 */
> > +		grp->des_str_sz = grp->info->num_des * 11 + 1;
> > +		grp->des_str = devm_kzalloc(dev, grp->des_str_sz, GFP_KERNEL);
> > +		if (!grp->des_str)
> > +			return -ENOMEM;
> > +
> > +		/* Reset group DE counter */
> > +		grp->info->num_des = 0;
> > +	}
> > +
> > +	/* Scan DEs and populate DE IDs arrays for all groups */
> > +	for (int i = 0; i < ti->info.base.num_des; i++) {
> > +		struct scmi_telemetry_group *grp = ti->info.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 compsing DES string */
> > +	for (int i = 0; i < ti->info.base.num_groups; i++) {
> > +		struct scmi_telemetry_group *grp = &ti->info.groups[i];
> > +		char *buf = grp->des_str;
> > +		size_t bufsize = grp->des_str_sz;
> > +
> > +		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%04X%c",
> > +					ti->info.des[grp->des[j]]->info->id, term);
> > +
> > +			buf += len;
> > +			bufsize -= len;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int
> > +scmi_telemetry_de_descriptors_get(const struct scmi_protocol_handle *ph,
> > +				  struct telemetry_info *ti)
> > +{
> > +	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;
> > +
> > +	xa_init(&ti->xa_des);
> > +	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 scmi_telemetry_de_groups_init(ph->dev, ti);
> > +}
> > +
> > +static int scmi_telemetry_enumerate_de(const struct scmi_protocol_handle *ph,
> > +				       struct telemetry_info *ti)
> > +{
> > +	int ret;
> > +
> > +	if (!ti->info.base.num_des)
> > +		return 0;
> > +
> > +	for (int i = 0; i < ti->info.base.num_des; i++) {
> > +		struct telemetry_de *tde;
> > +
> > +		tde = devm_kzalloc(ph->dev, sizeof(*tde), GFP_KERNEL);
> > +		if (!tde)
> > +			return -ENOMEM;
> > +
> > +		mutex_init(&tde->mtx);
> > +
> > +		/* Bind contiguous DE info structures */
> > +		tde->de.info = &ti->info.des_store[i];
> > +		ti->info.des[i] = &tde->de;
> > +	}
> > +
> > +	ret = scmi_telemetry_de_descriptors_get(ph, ti);
> > +	if (ret) {
> > +		dev_err(ph->dev, "Cannot get DE descriptors");
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +struct scmi_tlm_ivl_priv {
> > +	struct device *dev;
> > +	struct scmi_tlm_intervals **intrvs;
> > +	unsigned int grp_id;
> > +	unsigned int flags;
> > +};
> 
> I would also move all such structures into top of the file,
> into a dedicated section.

Yes, this is definitely a leftover.

> 
> > +
> > +static void iter_intervals_prepare_message(void *message,
> > +					   unsigned int desc_index,
> > +					   const void *priv)
> > +{
> > +	struct scmi_msg_telemetry_update_intervals *msg = message;
> > +	const struct scmi_tlm_ivl_priv *p = priv;
> > +
> > +	msg->index = cpu_to_le32(desc_index);
> > +	msg->group_identifier = cpu_to_le32(p->grp_id);
> > +	msg->flags = FIELD_PREP(GENMASK(3, 0), p->flags);
> > +}
> > +

[snip]

> > +static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,
> > +					   struct payload __iomem *payld,
> > +					   struct telemetry_shmti *shmti,
> > +					   bool update)
> > +{
> > +	bool ts_valid = TS_VALID(payld);
> > +	struct scmi_telemetry_de *de;
> > +	struct telemetry_de *tde;
> > +	u64 val, tstamp = 0;
> > +	u32 id;
> > +
> > +	id = PAYLD_ID(payld);
> > +	de = xa_load(&ti->xa_des, id);
> > +	if (!de)
> > +		return;
> > +
> > +	tde = to_tde(de);
> > +	/* Update DE location refs if requested: normally done only on enable */
> > +	if (update) {
> > +		tde->base = shmti->base;
> > +		tde->eplg = SHMTI_EPLG(shmti);
> > +		tde->offset = (void *)payld - (void *)shmti->base;
> > +	}
> > +
> > +	scoped_guard(mutex, &tde->mtx) {
> > +		if (tde->last_magic == shmti->last_magic)
> > +			return;
> > +	}
> > +
> > +	/* Data is always valid since we are NOT handling BLK TS lines here */
> > +	val = LINE_DATA_GET(&payld->l);
> > +	/* Collect the right TS */
> > +	if (ts_valid) {
> > +		if (USE_LINE_TS(payld)) {
> > +			tstamp = LINE_TSTAMP_GET(&payld->tsl);
> > +		} else if (USE_BLK_TS(payld)) {
> > +			if (!tde->bts) {
> > +				/*
> > +				 * Scanning a TDCF looking for the nearest
> > +				 * previous valid BLK_TS, after having found a
> > +				 * USE_BLK_TS() payload, MUST succeed.
> > +				 */
> > +				tde->bts = scmi_telemetry_blkts_bind(ti->dev,
> > +								     shmti, payld,
> > +								     &ti->xa_bts);
> > +				if (WARN_ON(!tde->bts))
> 
> Do we have to everytime log this when it happens?
> It looks like more a debug thing, isn't it?

Yes indeed...also the BLK_TS lines are more immature in the
implementation and alreasy changed in the spec...I will drop this

> 
> > +					return;
> > +			}
> > +
> > +			tstamp = scmi_telemetry_blkts_read(tde->last_magic,
> > +							   tde->bts);
> > +		}
> > +	}
> > +
> > +	guard(mutex)(&tde->mtx);
> > +	tde->last_magic = shmti->last_magic;
> > +	tde->last_val = val;
> > +	if (de->tstamp_enabled)
> > +		tde->last_ts = tstamp;
> > +	else
> > +		tde->last_ts = 0;
> > +}
> > +
> > +static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,
> > +					  struct payload __iomem *payld,
> > +					  struct telemetry_shmti *shmti,
> > +					  bool update)
> > +{
> > +	int used_qwords;
> > +
> > +	used_qwords = (USE_LINE_TS(payld) && TS_VALID(payld)) ?
> > +		QWORDS_TS_LINE_DATA_PAYLD : QWORDS_LINE_DATA_PAYLD;
> > +
> > +	/*Invalid lines are not an error, could simply be disabled DEs */
> > +	if (DATA_INVALID(payld))
> > +		return used_qwords;
> > +
> > +	if (!IS_BLK_TS(payld))
> > +		scmi_telemetry_tdcf_data_parse(ti, payld, shmti, update);
> > +	else
> > +		scmi_telemetry_tdcf_blkts_parse(ti, payld, shmti);
> > +
> > +	return used_qwords;
> > +}
> > +
> > +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
> > +				     unsigned int shmti_id, u64 ts,
> > +				     bool update)
> > +{
> > +	struct telemetry_shmti *shmti = &ti->shmti[shmti_id];
> > +	struct tdcf __iomem *tdcf = shmti->base;
> > +	int retries = SCMI_TLM_TDCF_MAX_RETRIES;
> > +	u64 startm = 0, endm = 0xffffffffffffffff;
> > +	void *eplg = SHMTI_EPLG(shmti);
> > +
> > +	if (!tdcf)
> > +		return -ENODEV;
> > +
> > +	do {
> > +		unsigned int qwords;
> > +		void __iomem *next;
> > +
> > +		/* A bit of exponential backoff between retries */
> > +		fsleep((SCMI_TLM_TDCF_MAX_RETRIES - retries) * 1000);
> 
> This is something that I would like to understand more.
> 

TDCF Shared memory area embed a pair of always increasing SEQ_NUMBERS
that are updated in sequence by the platform when the TDCF data are
updatred...this is to enable the agent to spot corruption due to an
agent read performed during a platform update...so it is possible that 

> > +
> > +		startm = TDCF_START_SEQ_GET(tdcf);

... we read the startm initial sequence number

> > +		if (IS_BAD_START_SEQ(startm))
> > +			continue;
> > +
> > +		/* On a BAD_SEQ this will be updated on the next attempt */
> > +		shmti->last_magic = startm;
> > +
> > +		qwords = tdcf->prlg.num_qwords;
> > +		next = tdcf->payld;
> > +		while (qwords) {
> > +			int used_qwords;
> > +
> > +			used_qwords = scmi_telemetry_tdcf_line_parse(ti, next,
> > +								     shmti, update);
> > +			if (qwords < used_qwords)
> > +				return -EINVAL;
> > +
> > +			next += used_qwords * 8;
> > +			qwords -= used_qwords;
> > +		}

..we gather the data...

> > +
> > +		endm = TDCF_END_SEQ_GET(eplg);
> > +	} while (startm != endm && --retries);

..we read the final seq_num and check...if there is a mismatch we haev
to retry.... 

...the above fsleep implements an exponential backoff timer (ethernet
style-ish) in order to reduce the odds of hitting multiple times in a
row such a race ...

> > +
> > +	if (startm != endm)
> > +		return -EPROTO;
> > +
> > +	return 0;
> > +}
> > +
> 
> I will take a few steps in this big patch ;)

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2025-10-28 17:52 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-25 20:35 [PATCH 00/10] Introduce SCMI Telemetry support Cristian Marussi
2025-09-25 20:35 ` [PATCH 01/10] firmware: arm_scmi: Define a common SCMI_MAX_PROTOCOLS value Cristian Marussi
2025-09-25 20:35 ` [PATCH 02/10] firmware: arm_scmi: Reduce the scope of protocols mutex Cristian Marussi
2025-10-17 15:07   ` Jonathan Cameron
2025-10-21  9:36     ` Cristian Marussi
2025-09-25 20:35 ` [PATCH 03/10] firmware: arm_scmi: Allow protocols to register for notifications Cristian Marussi
2025-10-17 15:10   ` Jonathan Cameron
2025-10-21  9:37     ` Cristian Marussi
2025-09-25 20:35 ` [PATCH 04/10] uapi: Add ARM SCMI definitions Cristian Marussi
2025-09-26 14:30   ` kernel test robot
2025-10-17 15:14   ` Jonathan Cameron
2025-10-21  9:40     ` Cristian Marussi
2025-09-25 20:35 ` [PATCH 05/10] firmware: arm_scmi: Add Telemetry protocol support Cristian Marussi
2025-09-26 17:27   ` kernel test robot
2025-10-17 15:37   ` Jonathan Cameron
2025-10-21 10:08     ` Cristian Marussi
2025-10-28 11:43   ` Lukasz Luba
2025-10-28 17:51     ` Cristian Marussi
2025-09-25 20:35 ` [PATCH 06/10] firmware: arm_scmi: Add System Telemetry driver Cristian Marussi
2025-10-20 16:23   ` Jonathan Cameron
2025-10-21 10:27     ` Cristian Marussi
2025-10-21 15:15       ` Jonathan Cameron
2025-10-21 16:03         ` Cristian Marussi
2025-10-24 10:33           ` Jonathan Cameron
2025-09-25 20:35 ` [PATCH 07/10] firmware: arm_scmi: Add System Telemetry ioctls support Cristian Marussi
2025-10-20 16:30   ` Jonathan Cameron
2025-10-21 10:30     ` Cristian Marussi
2025-09-25 20:35 ` [PATCH 08/10] firmware: arm_scmi: Add Telemetry components view Cristian Marussi
2025-09-25 20:35 ` [PATCH 09/10] include: trace: Add Telemetry trace events Cristian Marussi
2025-09-25 20:35 ` [PATCH 10/10] firmware: arm_scmi: Use new Telemetry traces Cristian Marussi
2025-09-26 13:05   ` kernel test robot
2025-09-26 19:54   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).