From nobody Thu Apr 2 10:43:03 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5112036EA8F; Sun, 29 Mar 2026 16:34:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774802089; cv=none; b=KM3O7VmCZr3x3f4/9xQOqFcpyiN0Koj3jeOEyNw8h50LSWYRUwLtGBK9rhzOCPxAsR8vRhyy8AugRqSW9irwMhUXcMzskJhmQtclyQuyayYtEEGS5BjnF98ICVOYEe4q4oWU6T43/dAUqhyRjV5i029Sli1y166wIRM3lIgTbP0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774802089; c=relaxed/simple; bh=n0ArTGprVZ7cpOMp9pRz+whmtoAPWuMcD8Qgco0fuic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X6xLrHPTKsysZp9xAc1qntaY8zdY1MwMSYv+rhKSlLQO7bk7DBVRdKdMoqoSlF2e06/swOBNkGOjIBnTL3azfb5k5y/NMHFf8G9QLWs91hlBtMRMVIbPjVTZaM42z4cYqdwPXZ/18piHZW2oCoX+r9uXVqXnaubcxLlszXvRc+s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=EdGs2N+U; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="EdGs2N+U" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B6C132A31; Sun, 29 Mar 2026 09:34:40 -0700 (PDT) Received: from pluto.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 938423F915; Sun, 29 Mar 2026 09:34:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1774802086; bh=n0ArTGprVZ7cpOMp9pRz+whmtoAPWuMcD8Qgco0fuic=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EdGs2N+UYGy+veoq3NLeZYDbuKTFwWNu+jMR3ZBfgLwri3Ar3LLTcrUWFCxQLFFIo QWyLyvLMKM87DWMIj8oPQZfXOsa85hE7wX9h4BLkObIrLKOzYQlvZEh3/Wez5QjxlC VYRV7jUM9QOPa2Ll7MNZZ7OnouCwKh68av4w64KI= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-doc@vger.kernel.org Cc: sudeep.holla@kernel.org, james.quinlan@broadcom.com, f.fainelli@gmail.com, vincent.guittot@linaro.org, etienne.carriere@st.com, peng.fan@oss.nxp.com, michal.simek@amd.com, dan.carpenter@linaro.org, d-gole@ti.com, jonathan.cameron@huawei.com, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, brauner@kernel.org, souvik.chakravarty@arm.com, Cristian Marussi Subject: [PATCH v3 10/24] firmware: arm_scmi: Add Telemetry configuration operations Date: Sun, 29 Mar 2026 17:33:21 +0100 Message-ID: <20260329163337.637393-11-cristian.marussi@arm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260329163337.637393-1-cristian.marussi@arm.com> References: <20260329163337.637393-1-cristian.marussi@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support for basic Telemetry configuration operations to selectively enable or disable DataEvents monitoring. Signed-off-by: Cristian Marussi --- v2 --> v3 - split from monolithic Telemetry patch - simplify clenaup with scmi_telemetry_de_unlink --- drivers/firmware/arm_scmi/telemetry.c | 348 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 16 ++ 2 files changed, 364 insertions(+) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index 6c5a988b2aac..9536262bbdf7 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -457,6 +457,9 @@ struct telemetry_info { static struct scmi_telemetry_res_info * __scmi_telemetry_resources_get(struct telemetry_info *ti); =20 +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, + unsigned int shmti_id, enum scan_mode mode); + static struct telemetry_de * scmi_telemetry_free_tde_get(struct telemetry_info *ti) { @@ -1687,10 +1690,355 @@ static int scmi_telemetry_shmti_scan(struct teleme= try_info *ti, return 0; } =20 +static int scmi_telemetry_group_state_update(struct telemetry_info *ti, + struct scmi_telemetry_group *grp, + bool *enable, bool *tstamp) +{ + struct scmi_telemetry_res_info *rinfo; + + rinfo =3D ti->res_get(ti); + for (int i =3D 0; i < grp->info->num_des; i++) { + struct scmi_telemetry_de *de =3D rinfo->des[grp->des[i]]; + + if (enable) + de->enabled =3D *enable; + if (tstamp) + de->tstamp_enabled =3D *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 =3D r; + u32 sid =3D le32_to_cpu(resp->shmti_id); + + /* Update DE SHMTI and offset, if applicable */ + if (IS_SHMTI_ID_VALID(sid)) { + if (sid >=3D 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 de_offs; + + de_offs =3D le32_to_cpu(resp->shmti_de_offset); + if (de_offs >=3D ti->shmti[sid].len - de->info->data_sz) + return -EPROTO; + + tde =3D to_tde(de); + tde->base =3D ti->shmti[sid].base; + tde->offset =3D de_offs; + /* A handy reference to the Epilogue updated */ + tde->eplg =3D SHMTI_EPLG(&ti->shmti[sid]); + + payld =3D tde->base + tde->offset; + if (USE_BLK_TS(payld) && !tde->bts) { + struct payload *bts_payld; + u32 bts_offs; + + bts_offs =3D le32_to_cpu(resp->blk_ts_offset); + bts_payld =3D (bts_offs) ? tde->base + bts_offs : NULL; + tde->bts =3D scmi_telemetry_blkts_bind(ti->ph->dev, + &ti->shmti[sid], + payld, + &ti->xa_lines, + bts_payld); + if (WARN_ON(!tde->bts)) + return -EPROTO; + } + } else { + int ret; + + /* + * A full SHMTI scan is needed when enabling a + * group or its timestamps in order to retrieve + * offsets: note 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. + */ + ret =3D scmi_telemetry_shmti_scan(ti, sid, SCAN_UPDATE); + if (ret) + dev_warn(ti->ph->dev, + "Failed group-scan of SHMTI ID:%d - ret:%d\n", + sid, ret); + } + } else if (!is_group) { + /* Unlink the related BLK_TS/UUID lines on disable */ + scmi_telemetry_de_unlink(de); + } + + return 0; +} + +static int __scmi_telemetry_state_set(const struct scmi_protocol_handle *p= h, + 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 =3D 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 =3D=3D *enabled_state) && + (!tstamp || *tstamp =3D=3D *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 =3D *tstamp; + return 0; + } + + if (!is_group) { + de =3D obj; + obj_id =3D de->info->id; + } else { + grp =3D obj; + obj_id =3D grp->info->id; + } + + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_DE_CONFIGURE, + sizeof(*msg), sizeof(*resp), &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + /* Note that BOTH DE and GROUPS have a first ID field.. */ + msg->id =3D cpu_to_le32(obj_id); + /* Default to disable mode for one DE */ + msg->flags =3D DE_DISABLE_ONE; + msg->flags |=3D 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 |=3D *tstamp ? DE_ENABLE_WTH_TSTAMP : + DE_ENABLE_NO_TSTAMP; + } else { + msg->flags |=3D *tstamp_enabled_state ? + DE_ENABLE_WTH_TSTAMP : DE_ENABLE_NO_TSTAMP; + } + } + + resp =3D t->rx.buf; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + ret =3D scmi_telemetry_state_set_resp_process(ti, de, resp, is_group); + if (!ret) { + /* Update cached state on success */ + if (enable) + *enabled_state =3D *enable; + if (tstamp) + *tstamp_enabled_state =3D *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 =3D ph->get_priv(ph); + struct scmi_telemetry_de *de; + + if (!enabled || !tstamp_enabled) + return -EINVAL; + + de =3D xa_load(&ti->xa_des, id); + if (!de) + return -ENODEV; + + *enabled =3D de->enabled; + *tstamp_enabled =3D 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) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + bool *enabled_state, *tstamp_enabled_state; + struct scmi_telemetry_res_info *rinfo; + void *obj; + + rinfo =3D ti->res_get(ti); + if (!is_group) { + struct scmi_telemetry_de *de; + + de =3D xa_load(&ti->xa_des, id); + if (!de) + return -ENODEV; + + enabled_state =3D &de->enabled; + tstamp_enabled_state =3D &de->tstamp_enabled; + obj =3D de; + } else { + struct scmi_telemetry_group *grp; + + if (id >=3D ti->info.base.num_groups) + return -EINVAL; + + grp =3D &rinfo->grps[id]; + + enabled_state =3D &grp->enabled; + tstamp_enabled_state =3D &grp->tstamp_enabled; + obj =3D 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 *p= h, + bool is_group) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + struct scmi_msg_telemetry_de_configure *msg; + struct scmi_telemetry_res_info *rinfo; + struct scmi_xfer *t; + int ret; + + rinfo =3D ti->res_get(ti); + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_DE_CONFIGURE, + sizeof(*msg), 0, &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->flags =3D DE_DISABLE_ALL; + if (is_group) + msg->flags |=3D GROUP_SELECTOR; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + for (int i =3D 0; i < ti->info.base.num_des; i++) + rinfo->des[i]->enabled =3D false; + + if (is_group) { + for (int i =3D 0; i < ti->info.base.num_groups; i++) + rinfo->grps[i].enabled =3D 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 =3D 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 =3D=3D SCMI_TLM_NOTIFICATION && + !ti->info.continuos_update_support) + return -EINVAL; + + if (res_id !=3D SCMI_TLM_GRP_INVALID && res_id >=3D ti->info.base.num_gro= ups) + return -EINVAL; + + if (res_id =3D=3D SCMI_TLM_GRP_INVALID || grp_ignore) { + active_update_interval =3D &ti->info.active_update_interval; + current_mode =3D &ti->info.current_mode; + } else { + struct scmi_telemetry_res_info *rinfo; + + rinfo =3D ti->res_get(ti); + active_update_interval =3D + &rinfo->grps[res_id].active_update_interval; + current_mode =3D &rinfo->grps[res_id].current_mode; + } + + if (!enable && !update_interval_ms && (!mode || *mode =3D=3D *current_mod= e)) + return 0; + + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_SET, + sizeof(*msg), 0, &t); + if (ret) + return ret; + + if (!update_interval_ms) + interval =3D cpu_to_le32(*active_update_interval); + else + interval =3D *update_interval_ms; + + tlm_enable =3D enable ? *enable : ti->info.enabled; + next_mode =3D mode ? *mode : *current_mode; + + msg =3D t->tx.buf; + msg->grp_id =3D res_id; + msg->control =3D tlm_enable ? TELEMETRY_ENABLE : 0; + msg->control |=3D grp_ignore ? TELEMETRY_SET_SELECTOR_ALL : + TELEMETRY_SET_SELECTOR_GROUP; + msg->control |=3D TELEMETRY_MODE_SET(next_mode); + msg->sampling_rate =3D interval; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + ti->info.enabled =3D tlm_enable; + *current_mode =3D next_mode; + ti->info.notif_enabled =3D *current_mode =3D=3D SCMI_TLM_NOTIFICATION; + if (update_interval_ms) + *active_update_interval =3D interval; + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + static const struct scmi_telemetry_proto_ops tlm_proto_ops =3D { .info_get =3D scmi_telemetry_info_get, .de_lookup =3D scmi_telemetry_de_lookup, .res_get =3D scmi_telemetry_resources_get, + .state_get =3D scmi_telemetry_state_get, + .state_set =3D scmi_telemetry_state_set, + .all_disable =3D scmi_telemetry_all_disable, + .collection_configure =3D scmi_telemetry_collection_configure, }; =20 /** diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index fcb45bd4b44c..03aa6b3dbd6b 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -942,6 +942,12 @@ struct scmi_telemetry_info { * @info_get: get the general Telemetry information. * @de_lookup: get a specific DE descriptor from the DE id. * @res_get: get a reference to the Telemetry resources descriptor. + * @state_get: retrieve the specific DE or GROUP state. + * @state_set: enable/disable the specific DE or GROUP with or without tim= estamps. + * @all_disable: disable ALL DEs or GROUPs. + * @collection_configure: choose a sampling rate and enable SHMTI/FC sampl= ing + * for on demand collection via @de_data_read or async + * notificatioins for all the enabled DEs. */ struct scmi_telemetry_proto_ops { const struct scmi_telemetry_info __must_check *(*info_get) @@ -950,6 +956,16 @@ struct scmi_telemetry_proto_ops { (const struct scmi_protocol_handle *ph, u32 id); const struct scmi_telemetry_res_info __must_check *(*res_get) (const struct scmi_protocol_handle *ph); + 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); }; =20 /** --=20 2.53.0