[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260114114638.2290765-13-cristian.marussi@arm.com>
Date: Wed, 14 Jan 2026 11:46:16 +0000
From: Cristian Marussi <cristian.marussi@....com>
To: linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
arm-scmi@...r.kernel.org,
linux-fsdevel@...r.kernel.org
Cc: sudeep.holla@....com,
james.quinlan@...adcom.com,
f.fainelli@...il.com,
vincent.guittot@...aro.org,
etienne.carriere@...com,
peng.fan@....nxp.com,
michal.simek@....com,
dan.carpenter@...aro.org,
d-gole@...com,
jonathan.cameron@...wei.com,
elif.topuz@....com,
lukasz.luba@....com,
philip.radford@....com,
souvik.chakravarty@....com,
Cristian Marussi <cristian.marussi@....com>
Subject: [PATCH v2 12/17] firmware: arm_scmi: Add Telemetry components view
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@....com>
---
v1 --> v2
- Use new FS API
- Introduce new stlmfs_lookup_by_name helper
---
.../firmware/arm_scmi/scmi_system_telemetry.c | 684 ++++++++++++++++++
1 file changed, 684 insertions(+)
diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
index 721de615bec3..1221520356fd 100644
--- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -174,6 +174,7 @@ struct scmi_tlm_inode {
* @top_dentry: A reference to the top dentry for this instance.
* @des_dentry: A reference to the DES dentry for this instance.
* @grps_dentry: A reference to the groups dentry for this instance.
+ * @compo_dentry: A reference to the components dentry for this instance.
* @info: A handy reference to this instance SCMI Telemetry info data.
*
*/
@@ -188,6 +189,7 @@ struct scmi_tlm_instance {
struct dentry *top_dentry;
struct dentry *des_dentry;
struct dentry *grps_dentry;
+ struct dentry *compo_dentry;
const struct scmi_telemetry_info *info;
};
@@ -196,6 +198,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 struct inode *stlmfs_get_inode(struct super_block *sb)
{
struct inode *inode = new_inode(sb);
@@ -815,6 +1337,18 @@ DEFINE_TLM_CLASS(persistent_tlmo, "persistent", 0,
DEFINE_TLM_CLASS(value_tlmo, "value", 0,
S_IFREG | S_IRUSR, &de_read_fops, NULL);
+static inline struct dentry *
+stlmfs_lookup_by_name(struct dentry *parent, const char *dname)
+{
+ struct qstr qstr;
+
+ qstr.name = dname;
+ qstr.len = strlen(dname);
+ qstr.hash = full_name_hash(parent, qstr.name, qstr.len);
+
+ return d_lookup(parent, &qstr);
+}
+
static int scmi_telemetry_de_populate(struct super_block *sb,
struct scmi_tlm_setup *tsp,
struct dentry *parent,
@@ -1659,6 +2193,150 @@ static struct dentry *stlmfs_create_root_dentry(struct super_block *sb)
return dentry;
}
+static int scmi_telemetry_de_subdir_symlink(struct super_block *sb,
+ struct scmi_tlm_setup *tsp,
+ const struct scmi_telemetry_de *de,
+ struct dentry *parent)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+ int ret;
+
+ if (IS_ERR(parent))
+ return 0;
+
+ char *name __free(kfree) = kasprintf(GFP_KERNEL, "0x%08X", de->info->id);
+ if (!name)
+ return -ENOMEM;
+
+ char *link __free(kfree) =
+ kasprintf(GFP_KERNEL, "../../../../../des/0x%08X", de->info->id);
+ if (!link)
+ return -ENOMEM;
+
+ dentry = simple_start_creating(parent, name);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ inode = stlmfs_get_inode(sb);
+ if (unlikely(!inode)) {
+ dev_err(tsp->dev,
+ "out of free dentries, cannot create '%s'", name);
+ return stlmfs_failed_creating(dentry);
+ }
+
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &simple_symlink_inode_operations;
+ inode_init_owner(&nop_mnt_idmap, inode, NULL, inode->i_mode);
+ inode->i_link = no_free_ptr(link);
+
+ //d_add(dentry, inode);
+ d_make_persistent(dentry, inode);
+
+ simple_done_creating(dentry);
+
+ return ret;
+}
+
+static struct dentry *
+scmi_telemetry_topology_path_get(struct super_block *sb,
+ struct scmi_tlm_setup *tsp,
+ struct dentry *parent, const char *dname)
+{
+ struct dentry *dentry;
+
+ dentry = stlmfs_lookup_by_name(parent, dname);
+ if (!dentry) {
+ struct scmi_tlm_class *dir_tlm_cls __free(kfree) =
+ kzalloc(sizeof(*dir_tlm_cls), GFP_KERNEL);
+ if (!dir_tlm_cls)
+ return NULL;
+
+ dir_tlm_cls->name = kasprintf(GFP_KERNEL, "%s", dname);
+ if (!dir_tlm_cls->name)
+ return NULL;
+
+ dir_tlm_cls->mode = S_IFDIR | S_IRWXU;
+ dir_tlm_cls->flags = TLM_IS_DYNAMIC;
+
+ dentry = stlmfs_create_dentry(sb, tsp, parent,
+ dir_tlm_cls, NULL);
+ if (!IS_ERR(dentry))
+ retain_and_null_ptr(dir_tlm_cls);
+ }
+
+ return dentry;
+}
+
+static int scmi_telemetry_topology_add_node(struct super_block *sb,
+ struct scmi_tlm_instance *ti,
+ const struct scmi_telemetry_de *de)
+{
+ struct dentry *ctype, *cinst, *cunit, *dinst;
+ struct scmi_tlm_de_info *dei = de->info;
+ char inst_str[32];
+ int ret;
+
+ /* by_compo_type/<COMPO_TYPE_STR>/ */
+ ctype = scmi_telemetry_topology_path_get(sb, ti->tsp, ti->compo_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, ti->tsp, 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, ti->tsp, 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, ti->tsp, cunit, inst_str);
+ dput(cunit);
+ if (!dinst)
+ return -ENOMEM;
+
+ ret = scmi_telemetry_de_subdir_symlink(sb, ti->tsp, de, dinst);
+ dput(dinst);
+
+ return ret;
+}
+
+DEFINE_TLM_CLASS(compo_dir_cls, "components", 0, S_IFDIR | S_IRWXU, NULL, NULL);
+
+static int scmi_telemetry_topology_view_add(struct scmi_tlm_instance *ti)
+{
+ const struct scmi_telemetry_res_info *rinfo;
+ struct scmi_tlm_setup *tsp = ti->tsp;
+ struct device *dev = tsp->dev;
+
+ rinfo = scmi_telemetry_res_info_get(tsp);
+ if (!rinfo || !rinfo->fully_enumerated)
+ return -ENODEV;
+
+ ti->compo_dentry =
+ stlmfs_create_dentry(ti->sb, tsp, ti->top_dentry, &compo_dir_cls, NULL);
+
+ for (int i = 0; i < rinfo->num_des; i++) {
+ int ret;
+
+ ret = scmi_telemetry_topology_add_node(ti->sb, ti, rinfo->des[i]);
+ if (ret)
+ dev_err(dev, "Fail to add node %s to topology. Skip.\n",
+ rinfo->des[i]->info->name);
+ }
+
+ return 0;
+}
+
static int scmi_tlm_root_dentries_initialize(struct scmi_tlm_instance *ti)
{
struct scmi_tlm_setup *tsp = ti->tsp;
@@ -1712,6 +2390,12 @@ static int scmi_telemetry_instance_register(struct super_block *sb,
ti->top_cls.name);
}
+ ret = scmi_telemetry_topology_view_add(ti);
+ if (ret)
+ dev_warn(ti->tsp->dev,
+ "Failed to create topology view for instance %s.\n",
+ ti->top_cls.name);
+
return 0;
}
--
2.52.0
Powered by blists - more mailing lists