[RFC PATCH 5/7] firmware: arm_scmi: Add System Telemetry chardev/ioctls API
Cristian Marussi
cristian.marussi at arm.com
Fri Jun 20 12:28:11 PDT 2025
Add to the SCMI Telemetry driver an alternative custom userspace API based
on a character device and ioctls.
Still lacking:
- a few groups related ioctls
- a telemetry-reset ioctl
- mmap support for raw telenetry data access
Signed-off-by: Cristian Marussi <cristian.marussi at arm.com>
---
.../firmware/arm_scmi/scmi_system_telemetry.c | 496 +++++++++++++++++-
include/uapi/linux/scmi.h | 253 +++++++++
2 files changed, 747 insertions(+), 2 deletions(-)
create mode 100644 include/uapi/linux/scmi.h
diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
index a2f59001747d..c354dbe8a0f7 100644
--- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -6,6 +6,7 @@
*/
#include <linux/atomic.h>
+#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
@@ -15,6 +16,8 @@
#include <linux/sysfs.h>
#include <linux/slab.h>
+#include <uapi/linux/scmi.h>
+
#define MAX_BULK_LINE_CHAR_LENGTH 64
struct scmi_tlm_setup;
@@ -39,13 +42,22 @@ struct scmi_tlm_de_dev {
#define to_tlm_de_dev(d) \
(container_of((d), struct scmi_tlm_de_dev, dev))
+struct scmi_tlm_ioctls_db {
+ struct scmi_tlm_info tlm_info;
+ struct scmi_tlm_intervals *tlm_intervals;
+ struct scmi_tlm_intervals **tlm_grp_intervals;
+ struct scmi_tlm_des_list *tlm_des_list;
+};
+
struct scmi_tlm_instance {
struct device dev;
+ struct cdev cdev;
struct device des_dev;
struct device groups_dev;
struct scmi_tlm_de_dev **des;
struct scmi_tlm_setup *tsp;
const struct scmi_telemetry_info *info;
+ struct scmi_tlm_ioctls_db io_db;
};
#define dev_to_tlm_instance(d) \
@@ -71,6 +83,8 @@ struct scmi_tlm_setup {
const void *priv;
};
+static int scmi_tlm_major;
+
static void scmi_telemetry_release(struct device *dev)
{
}
@@ -326,7 +340,6 @@ static ssize_t intervals_discrete_show(struct device *dev,
return __intervals_discrete_show(buf, ti->info->intervals.discrete);
}
-//TODO Review available interval show
#define BUF_SZ 1024
static inline ssize_t
__available_update_show(char *buf,
@@ -532,6 +545,31 @@ scmi_telemetry_dev_register(struct device *dev, struct device *parent,
return ret;
}
+static int
+scmi_telemetry_cdev_register(struct device *dev, struct device *parent,
+ struct cdev *cdev, const struct file_operations *fops,
+ const char *name, unsigned int minor)
+{
+ int ret;
+
+ dev->parent = parent;
+ dev->release = scmi_tlm_dev_release;
+ dev_set_name(dev, "%s", name);
+ device_set_pm_not_required(dev);
+ dev_set_uevent_suppress(dev, true);
+
+ device_initialize(dev);
+
+ dev->devt = MKDEV(scmi_tlm_major, minor);
+ cdev_init(cdev, fops);
+
+ ret = cdev_device_add(cdev, dev);
+ if (ret)
+ put_device(dev);
+
+ return ret;
+}
+
static int scmi_des_iter(struct device *dev, void *data)
{
device_unregister(dev);
@@ -807,6 +845,448 @@ static int scmi_telemetry_des_initialize(struct device *dev,
return ret;
}
+struct scmi_tlm_priv {
+ char *buf;
+ size_t buf_sz;
+ int buf_len;
+ struct scmi_tlm_instance *ti;
+};
+
+static int scmi_tlm_open(struct inode *ino, struct file *filp)
+{
+ struct scmi_tlm_instance *ti;
+ struct scmi_tlm_priv *tp;
+
+ tp = kzalloc(sizeof(*tp), GFP_KERNEL);
+ if (!tp)
+ return -ENOMEM;
+
+ ti = container_of(ino->i_cdev, struct scmi_tlm_instance, cdev);
+ tp->ti = ti;
+
+ filp->private_data = tp;
+
+ return 0;
+}
+
+static int scmi_tlm_bulk_buffer_allocate_and_fill(struct scmi_tlm_priv *tp)
+{
+ struct scmi_tlm_instance *ti = tp->ti;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ struct scmi_telemetry_de_sample *samples;
+ int ret, num_samples;
+
+ tp->buf_sz = ti->info->num_de * MAX_BULK_LINE_CHAR_LENGTH;
+ tp->buf = kzalloc(tp->buf_sz, GFP_KERNEL);
+ if (!tp->buf)
+ return -ENOMEM;
+
+ num_samples = ti->info->num_de;
+ samples = kcalloc(num_samples, sizeof(*samples), GFP_KERNEL);
+ if (!samples) {
+ kfree(tp->buf);
+ return -ENOMEM;
+ }
+
+ ret = tsp->ops->des_bulk_read(tsp->ph, SCMI_TLM_GRP_INVALID,
+ &num_samples, samples);
+ if (ret) {
+ kfree(samples);
+ kfree(tp->buf);
+ return ret;
+ }
+
+ ret = scmi_tlm_buffer_fill(&ti->dev, tp->buf, tp->buf_sz, &tp->buf_len,
+ num_samples, samples);
+ kfree(samples);
+
+ return ret;
+}
+
+static ssize_t scmi_tlm_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct scmi_tlm_priv *tp = filp->private_data;
+ int ret;
+
+ if (!tp->buf) {
+ ret = scmi_tlm_bulk_buffer_allocate_and_fill(tp);
+ if (ret)
+ return ret;
+ }
+
+ return simple_read_from_buffer(buf, count, ppos, tp->buf, tp->buf_len);
+}
+
+static __poll_t scmi_tlm_poll(struct file *, struct poll_table_struct *)
+{
+ return 0;
+}
+
+static long
+scmi_tlm_info_get_ioctl(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+
+ if (copy_to_user(uptr, &ti->io_db.tlm_info,
+ sizeof(ti->io_db.tlm_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long
+scmi_tlm_intervals_ioctl(struct scmi_tlm_instance *ti, unsigned long arg,
+ bool group)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_intervals ivs, *tlm_ivs;
+
+ if (copy_from_user(&ivs, uptr, sizeof(ivs)))
+ return -EFAULT;
+
+ if (!group) {
+ tlm_ivs = ti->io_db.tlm_intervals;
+ } else {
+ if (ivs.grp_id >= ti->info->num_groups)
+ return -EINVAL;
+
+ tlm_ivs = ti->io_db.tlm_grp_intervals[ivs.grp_id];
+ }
+
+ 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(struct scmi_tlm_instance *ti, unsigned long arg,
+ bool all)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ const struct scmi_telemetry_de *de;
+ 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 < ti->info->num_de; i++) {
+ de = ti->info->des[i];
+
+ ret = tsp->ops->state_set(tsp->ph, false, de->id,
+ (bool *)&tcfg.enable,
+ (bool *)&tcfg.t_enable);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static long
+scmi_tlm_de_config_get_ioctl(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ 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(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_config cfg;
+
+ cfg.enable = !!ti->info->enabled;
+ cfg.current_update_interval =
+ ti->info->intervals.active_update_interval;
+
+ if (copy_to_user(uptr, &cfg, sizeof(cfg)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long
+scmi_tlm_config_set_ioctl(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ struct scmi_tlm_config cfg = {};
+
+ if (copy_from_user(&cfg, uptr, sizeof(cfg)))
+ return -EFAULT;
+
+ return tsp->ops->collection_configure(tsp->ph, SCMI_TLM_GRP_INVALID,
+ true, (bool *)&cfg.enable,
+ &cfg.current_update_interval,
+ NULL);
+}
+
+static long
+scmi_tlm_des_list_get_ioctl(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_des_list dsl;
+
+ if (copy_from_user(&dsl, uptr, sizeof(dsl)))
+ return -EFAULT;
+
+ if (dsl.num_des < ti->io_db.tlm_des_list->num_des)
+ return -EFAULT;
+
+ if (copy_to_user(uptr, ti->io_db.tlm_des_list,
+ sizeof(*ti->io_db.tlm_des_list) +
+ ti->io_db.tlm_des_list->num_des * sizeof(ti->io_db.tlm_des_list->des[0])))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long
+scmi_tlm_de_value_get_ioctl(struct scmi_tlm_instance *ti, unsigned long arg)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ 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_des_read_ioctl(struct scmi_tlm_instance *ti,
+ unsigned long arg, bool single)
+{
+ void * __user uptr = (void * __user)arg;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ struct scmi_tlm_bulk_read bulk, *bulk_ptr;
+ int ret;
+
+ if (copy_from_user(&bulk, uptr, sizeof(bulk)))
+ return -EFAULT;
+
+ bulk_ptr = kzalloc(sizeof(*bulk_ptr) +
+ bulk.num_samples * sizeof(bulk_ptr->samples[0]),
+ GFP_KERNEL);
+ if (!bulk_ptr)
+ return -ENOMEM;
+
+ bulk_ptr->grp_id = bulk.grp_id;
+ bulk_ptr->num_samples = bulk.num_samples;
+ if (!single)
+ ret = tsp->ops->des_bulk_read(tsp->ph, bulk_ptr->grp_id,
+ &bulk_ptr->num_samples,
+ (struct scmi_telemetry_de_sample *)bulk_ptr->samples);
+ else
+ ret = tsp->ops->des_sample_get(tsp->ph, bulk_ptr->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_priv *tp = filp->private_data;
+ struct scmi_tlm_instance *ti = tp->ti;
+
+ switch (cmd) {
+ case SCMI_TLM_GET_INFO:
+ return scmi_tlm_info_get_ioctl(ti, arg);
+ case SCMI_TLM_GET_CFG:
+ return scmi_tlm_config_get_ioctl(ti, arg);
+ case SCMI_TLM_SET_CFG:
+ return scmi_tlm_config_set_ioctl(ti, arg);
+ case SCMI_TLM_GET_INTRVS:
+ return scmi_tlm_intervals_ioctl(ti, arg, false);
+ case SCMI_TLM_GET_DE_CFG:
+ return scmi_tlm_de_config_get_ioctl(ti, arg);
+ case SCMI_TLM_SET_DE_CFG:
+ return scmi_tlm_de_config_set_ioctl(ti, arg, false);
+ case SCMI_TLM_GET_DE_INFO:
+ return -EOPNOTSUPP;
+ case SCMI_TLM_GET_DE_LIST:
+ return scmi_tlm_des_list_get_ioctl(ti, arg);
+ case SCMI_TLM_GET_DE_VALUE:
+ return scmi_tlm_de_value_get_ioctl(ti, arg);
+ case SCMI_TLM_GET_GRP_CFG:
+ return -EOPNOTSUPP;
+ case SCMI_TLM_SET_GRP_CFG:
+ return -EOPNOTSUPP;
+ case SCMI_TLM_GET_GRP_INTRVS:
+ return scmi_tlm_intervals_ioctl(ti, arg, true);
+ case SCMI_TLM_GET_GRP_INFO:
+ return -EOPNOTSUPP;
+ case SCMI_TLM_GET_GRP_LIST:
+ return -EOPNOTSUPP;
+ case SCMI_TLM_SINGLE_SAMPLE:
+ return scmi_tlm_des_read_ioctl(ti, arg, true);
+ case SCMI_TLM_BULK_READ:
+ return scmi_tlm_des_read_ioctl(ti, arg, false);
+ case SCMI_TLM_SET_ALL_CFG:
+ return scmi_tlm_de_config_set_ioctl(ti, arg, true);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static long scmi_tlm_compat_ioctl(struct file *, unsigned int, unsigned long)
+{
+ return 0;
+}
+
+static int scmi_tlm_mmap(struct file *, struct vm_area_struct *)
+{
+ return 0;
+}
+
+static int scmi_tlm_release(struct inode *ino, struct file *filp)
+{
+ struct scmi_tlm_priv *tp = filp->private_data;
+
+ kfree(tp->buf);
+ kfree(tp);
+
+ return 0;
+}
+
+static const struct file_operations scmi_tlm_fops = {
+ .owner = THIS_MODULE,
+ .open = scmi_tlm_open,
+ .read = scmi_tlm_read,
+ .poll = scmi_tlm_poll,
+ .unlocked_ioctl = scmi_tlm_unlocked_ioctl,
+ .compat_ioctl = scmi_tlm_compat_ioctl,
+ .mmap = scmi_tlm_mmap,
+ .release = scmi_tlm_release,
+};
+
+static int scmi_tlm_setup_ioctl_data(struct device *dev,
+ struct scmi_tlm_instance *ti)
+{
+ ti->io_db.tlm_info.version = ti->info->version;
+ for (int i = 0; i < SCMI_TLM_DE_IMPL_VERS; i++)
+ ti->io_db.tlm_info.de_impl_version[i] = ti->info->de_impl_version[i];
+ ti->io_db.tlm_info.num_des = ti->info->num_de;
+ ti->io_db.tlm_info.num_groups = ti->info->num_groups;
+ ti->io_db.tlm_info.num_intervals = ti->info->intervals.num;
+ if (ti->info->reset_support)
+ ti->io_db.tlm_info.flags = SCMI_TLM_CAN_RESET;
+
+ ti->io_db.tlm_intervals = devm_kzalloc(dev, sizeof(*ti->io_db.tlm_intervals) +
+ ti->info->intervals.num * sizeof(__u32),
+ GFP_KERNEL);
+ if (!ti->io_db.tlm_intervals)
+ return -ENOMEM;
+
+ ti->io_db.tlm_intervals->grp_id = 0;
+ ti->io_db.tlm_intervals->discrete = ti->info->intervals.discrete;
+ ti->io_db.tlm_intervals->num = ti->info->intervals.num;
+ for (int i = 0; i < ti->info->intervals.num; i++)
+ ti->io_db.tlm_intervals->available[i] =
+ ti->info->intervals.update_intervals[i];
+
+ ti->io_db.tlm_grp_intervals = devm_kcalloc(dev, ti->info->num_groups,
+ sizeof(ti->io_db.tlm_grp_intervals),
+ GFP_KERNEL);
+ if (!ti->io_db.tlm_grp_intervals)
+ return -ENOMEM;
+
+ for (int i = 0; i < ti->info->num_groups; i++) {
+ struct scmi_tlm_intervals *ivs;
+ struct scmi_telemetry_group *grp = &ti->info->des_groups[i];
+
+ ivs = devm_kzalloc(dev, sizeof(*ivs) +
+ grp->intervals.num * sizeof(__u32),
+ GFP_KERNEL);
+ if (!ivs)
+ return -ENOMEM;
+
+ ivs->grp_id = i;
+ ivs->discrete = grp->intervals.discrete;
+ ivs->num = grp->intervals.num;
+ for (int j = 0; j < ivs->num; j++)
+ ivs->available[i] = grp->intervals.update_intervals[i];
+
+ ti->io_db.tlm_grp_intervals[i] = ivs;
+ }
+
+ ti->io_db.tlm_des_list = devm_kzalloc(dev, sizeof(*ti->io_db.tlm_des_list) +
+ ti->info->num_de * sizeof(ti->io_db.tlm_des_list->des[0]),
+ GFP_KERNEL);
+ if (!ti->io_db.tlm_des_list)
+ return -ENOMEM;
+
+ ti->io_db.tlm_des_list->num_des = ti->info->num_de;
+ for (int i = 0; i < ti->info->num_de; i++) {
+ ti->io_db.tlm_des_list->des[i].id = ti->info->des[i]->id;
+ ti->io_db.tlm_des_list->des[i].grp_id =
+ ti->info->des[i]->grp ? ti->info->des[i]->grp->id : SCMI_TLM_GRP_INVALID;
+ ti->io_db.tlm_des_list->des[i].data_sz = ti->info->des[i]->data_sz;
+ ti->io_db.tlm_des_list->des[i].type = ti->info->des[i]->type;
+ ti->io_db.tlm_des_list->des[i].unit = ti->info->des[i]->unit;
+ ti->io_db.tlm_des_list->des[i].unit_exp = ti->info->des[i]->unit_exp;
+ ti->io_db.tlm_des_list->des[i].tstamp_exp = ti->info->des[i]->tstamp_exp;
+ ti->io_db.tlm_des_list->des[i].instance_id = ti->info->des[i]->instance_id;
+ ti->io_db.tlm_des_list->des[i].compo_instance_id =
+ ti->info->des[i]->compo_instance_id;
+ ti->io_db.tlm_des_list->des[i].compo_type = ti->info->des[i]->compo_type;
+ ti->io_db.tlm_des_list->des[i].persistent = ti->info->des[i]->persistent;
+ if (ti->info->des[i]->name)
+ strscpy(ti->io_db.tlm_des_list->des[i].name, ti->info->des[i]->name,
+ SCMI_SHORT_NAME_MAX_SIZE);
+ }
+
+ return 0;
+}
+
static int
scmi_tlm_root_instance_initialize(struct device *dev,
struct scmi_tlm_instance *ti, int instance_id)
@@ -814,11 +1294,16 @@ scmi_tlm_root_instance_initialize(struct device *dev,
char name[16];
int ret;
+ ret = scmi_tlm_setup_ioctl_data(dev, ti);
+ if (ret)
+ return ret;
+
ti->dev.class = &scmi_telemetry_class;
ti->dev.groups = scmi_telemetry_groups;
snprintf(name, 16, "scmi_tlm_%d", instance_id);
- ret = scmi_telemetry_dev_register(&ti->dev, NULL, name);
+ ret = scmi_telemetry_cdev_register(&ti->dev, NULL, &ti->cdev,
+ &scmi_tlm_fops, name, instance_id);
if (ret)
return ret;
@@ -940,8 +1425,15 @@ static struct scmi_driver scmi_telemetry_driver = {
static int __init scmi_telemetry_init(void)
{
+ dev_t devt;
int ret;
+ ret = alloc_chrdev_region(&devt, 0, 1024, "scmi-tlm");
+ if (ret)
+ return ret;
+
+ scmi_tlm_major = MAJOR(devt);
+
ret = class_register(&scmi_telemetry_class);
if (ret)
return ret;
diff --git a/include/uapi/linux/scmi.h b/include/uapi/linux/scmi.h
new file mode 100644
index 000000000000..8a0f365fca52
--- /dev/null
+++ b/include/uapi/linux/scmi.h
@@ -0,0 +1,253 @@
+/* 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_VERS 4
+#define SCMI_TLM_GRP_INVALID 0xFFFFFFFF
+
+/**
+ * scmi_tlm_info - Basic info about an instance
+ *
+ * @version: SCMI Telemetry protocol version
+ * @de_impl_version: SCMI Telemetry DE implementation revision
+ * @num_desi: 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
+ */
+struct scmi_tlm_info {
+ __u32 version;
+ __u32 de_impl_version[SCMI_TLM_DE_IMPL_VERS];
+ __u32 num_des;
+ __u32 num_groups;
+ __u32 num_intervals;
+ __u32 flags;
+#define SCMI_TLM_CAN_RESET (1 << 0)
+};
+
+/**
+ * scmi_tlm_config - Basic instance configuration
+ *
+ * @enable: Enable/Disable Telemetry for the whole instance
+ * @current_update_interval: Get/Set currently active update interval
+ * (periodic tick for SHMTIs and Notifications)
+ *
+ * Used by:
+ * RO - SCMI_TLM_GET_CFG
+ * WO - SCMI_TLM_SET_CFG
+ */
+struct scmi_tlm_config {
+ __u32 enable;
+ __u32 current_update_interval;
+};
+
+/**
+ * scmi_tlm_intervals - Update intervals descriptor
+ *
+ * @grp_id: Group identifier (ignored by SCMI_TLM_GET_INTRVS)
+ * @discrete: Flag to indicate the nature of the intervals described in
+ * @available. When 'false' @available is a triplet: min/max/step
+ * @num: Number of entries of @available
+ * @available: A variably-sized array containing the update intervals
+ *
+ * Used by:
+ * RO - SCMI_TLM_GET_INTRVS
+ * RW - SCMI_TLM_GET_GRP_INTRVS
+ */
+struct scmi_tlm_intervals {
+ __u32 grp_id;
+ __u32 discrete;
+ __u32 num;
+ __u32 available[];
+};
+
+/**
+ * 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
+ */
+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
+ * RO - SCMI_TLM_GET_DE_LIST
+ */
+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:
+ * RO - SCMI_TLM_GET_DE_LIST
+ */
+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
+ * RO - SCMI_TLM_SINGLE_READ
+ */
+struct scmi_tlm_de_sample {
+ __u32 id;
+ __u64 tstamp;
+ __u64 val;
+};
+
+/**
+ * scmi_tlm_bulk_read - Bulk read of multiple DEs
+ *
+ * @grp_id: The identifier of the group to query with a single asynchronous
+ * sample read. Set to SCMI_TLM_GRP_INVALID to ignore.
+ * @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
+ */
+struct scmi_tlm_bulk_read {
+ __u32 grp_id;
+ __u32 num_samples;
+ struct scmi_tlm_de_sample samples[];
+};
+
+/**
+ * scmi_tlm_grp_info - DE-group descriptor
+ *
+ * @id: Group identifier
+ * @flags: Group capabilities
+ * @num_intervals: Number of update intervals supported
+ * @num_des: Number of DEs part of this group
+ *
+ * Used by:
+ * WR - SCMI_TLM_GET_GRP_INFO
+ */
+struct scmi_tlm_grp_info {
+ __u32 id;
+ __u32 flags;
+#define SCMI_TLM_GRP_HAS_UPDATE (1 << 0)
+ __u32 num_intervals;
+ __u32 num_des;
+};
+
+/**
+ * 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
+ */
+struct scmi_tlm_grps_list {
+ __u32 num_grps;
+ struct scmi_tlm_grp_info grps[];
+};
+
+/**
+ * scmi_tlm_grp_config - Group config
+ *
+ * @id: Identifier of the DEs-group to act upon
+ * @enable: A boolean to enable/disable the group
+ * @t_enable: A boolean to enable/disable the timestamp for this group
+ *
+ * Used by:
+ * RW - SCMI_TLM_GET_GRP_CFG
+ * WO - SCMI_TLM_SET_GRP_CFG
+ */
+struct scmi_tlm_grp_config {
+ __u32 id;
+ __u32 enable;
+ __u32 t_enable;
+ __u32 current_update_interval;
+};
+
+#define SCMI 0xF1
+
+#define SCMI_TLM_GET_INFO _IOR(SCMI, 0x00, struct scmi_tlm_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 _IOR(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 _IOW(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_GET_GRP_CFG _IOWR(SCMI, 0x09, struct scmi_tlm_grp_config)
+#define SCMI_TLM_SET_GRP_CFG _IOW(SCMI, 0x0A, struct scmi_tlm_grp_config)
+#define SCMI_TLM_GET_GRP_INTRVS _IOWR(SCMI, 0x0B, struct scmi_tlm_intervals)
+#define SCMI_TLM_GET_GRP_INFO _IOWR(SCMI, 0x0C, struct scmi_tlm_grp_info)
+#define SCMI_TLM_GET_GRP_LIST _IOR(SCMI, 0x0D, struct scmi_tlm_grps_list)
+#define SCMI_TLM_SINGLE_SAMPLE _IOWR(SCMI, 0x0E, struct scmi_tlm_bulk_read)
+#define SCMI_TLM_BULK_READ _IOWR(SCMI, 0x0F, struct scmi_tlm_bulk_read)
+#define SCMI_TLM_SET_ALL_CFG _IOW(SCMI, 0x10, struct scmi_tlm_de_config)
+
+#endif /* _UAPI_LINUX_SCMI_H */
--
2.47.0
More information about the linux-arm-kernel
mailing list