From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 524403E51F0; Sun, 20 Sep 2026 09:20:19 +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=1789896024; cv=none; b=M8FXWeBkkjwZw0/YT2xXWI8jtUWS0tqNyBELyrxaSUSL0XkLxsbRERedwwKmkVca0V0hnEVwDEe/kkxYfgdptB0NfCV8wqbFh4Er/GFsLSxqjT4VcI2eUSBUGFzdzpVxuX8upBcp/GHWFphlDjrySdrk21v1YdySL++84iaYeks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896024; c=relaxed/simple; bh=P0MSY/kDk0ow8kuKtSYBKr3uG3KhXyWBVbPVVFEhnOA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=N6wpQdMbKKVdmLQ7LT/AhDC06pb1PEYqzo0tJh22vWWXpTV+3AMG364cllyyTrYjlIim65Hjym6O5qYgXWZfa1xIq6xLxihbbczIf70hQYnAE8asYlq6mw5Nn8JEWD/9SFfKM2bK6VuXlmpedw4ffTmYsD58UmVsYHqPVu+2rXM= 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=UR4Q4NZh; 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="UR4Q4NZh" 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 F27D01691; Sun, 20 Sep 2026 02:20:09 -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 6A2BB3F59E; Sun, 20 Sep 2026 02:20:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896013; bh=P0MSY/kDk0ow8kuKtSYBKr3uG3KhXyWBVbPVVFEhnOA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UR4Q4NZhWn2Y2X904sw0onb09M6CGkumJqWgR8OczUv47NmVBLiDqglp+iX5rosWF epwGOz8Wj14Cc1twhNUeHYPr6y7AD9SMT0l/yUZkMvo9Zy0xw36jEP2rW3ZqwEkUMT fes5YkW9Jhzu8qHfuZrvdRwkRrBfKyl4YU+YLOVE= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Cameron Subject: [PATCH v12 01/25] firmware: arm_scmi: Add new SCMIv4.0 error codes definitions Date: Sun, 20 Sep 2026 10:19:04 +0100 Message-ID: <20260920091928.2014972-2-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" SCMIv4.0 introduces a couple of new possible protocol error codes: add the needed definitions and mappings to Linux error values. Reviewed-by: Jonathan Cameron Signed-off-by: Cristian Marussi Reviewed-by: David Hildenbrand (Arm) --- v4 --> v5 - map SCMI_ERR_IN_USE to EBUSY as natural --- drivers/firmware/arm_scmi/common.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi= /common.h index fe8c22cfb9f7..6f89ba96493d 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -48,6 +48,8 @@ enum scmi_error_codes { SCMI_ERR_GENERIC =3D -8, /* Generic Error */ SCMI_ERR_HARDWARE =3D -9, /* Hardware Error */ SCMI_ERR_PROTOCOL =3D -10,/* Protocol Error */ + SCMI_ERR_IN_USE =3D -11, /* In Use Error */ + SCMI_ERR_PARTIAL =3D -12, /* Partial Error */ }; =20 static const int scmi_linux_errmap[] =3D { @@ -63,6 +65,8 @@ static const int scmi_linux_errmap[] =3D { -EIO, /* SCMI_ERR_GENERIC */ -EREMOTEIO, /* SCMI_ERR_HARDWARE */ -EPROTO, /* SCMI_ERR_PROTOCOL */ + -EBUSY, /* SCMI_ERR_IN_USE */ + -EINVAL, /* SCMI_ERR_PARTIAL */ }; =20 static inline int scmi_to_linux_errno(int errno) --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4E5A43E832B; Sun, 20 Sep 2026 09:20:19 +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=1789896024; cv=none; b=gAYRDcTon5UFaEGeqyIQNuYW+WaoRWWCuTvBy60UxtF5TEnckSp802YL1hw7Lj4WrtvyyXH5fgdoS7kA2/8A7PuL+nITae8aqrTUwSCj4M8oXdFXwoN4NOn0A3hV+kSWvFQbWq/U8A71GLjhjFt0d/Ud4MuqcS0j8WF8Eyw3abw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896024; c=relaxed/simple; bh=Z1zT/+qmdnYlo6v6PyvGTRruroGKs4FxS/7jYNNL1h8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=edDYKjYT54Rf5rc4b91M1fSlsb9KeMvi4gVqGByG2aaYTU1CnajWTBocMjEQ09twlmZ9Z8c/pNfEY2bLJ3WWeDVAFIYK5k9UjTses1A3e0J43eujwk/5EuNKo/NOwOqwNQLH4YkQQr6k3gw6wNrw8jX7XVbny/ly6PEa1uQk8dU= 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=OtL7pd7t; 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="OtL7pd7t" 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 26889175A; Sun, 20 Sep 2026 02:20:14 -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 D368E3F59E; Sun, 20 Sep 2026 02:20:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896017; bh=Z1zT/+qmdnYlo6v6PyvGTRruroGKs4FxS/7jYNNL1h8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OtL7pd7tSdRuMX6Uj2n/EhNv5zenFA7EzbQCZ3/kabtNnyUF5Uq70SU18u5GP4Rnq IKYknV04kQcHjKTNIAiFzyVpK//cWJgW0iyumlsBETslgEB8O+trrlYvEzcHfz+a30 MJsBNRQGS16Ij5fKz6Fh/8RBFEam7TNWi54skhcM= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Cameron Subject: [PATCH v12 02/25] firmware: arm_scmi: Allow registration of unknown-size events/reports Date: Sun, 20 Sep 2026 10:19:05 +0100 Message-ID: <20260920091928.2014972-3-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" Allow protocols to register events with build-time unknown sizes: such events can be declared zero-sized and let the core SCMI stack perform the needed safe-net boundary checks based on the configured transport size. Reviewed-by: Jonathan Cameron Signed-off-by: Cristian Marussi --- v10 --> v11 - account for ktime_t field when checking boundary v2 --> v3 - split out of previous patch on protocol notifier - use max() instead of max_t() --- drivers/firmware/arm_scmi/notify.c | 25 ++++++++++++++++++++----- drivers/firmware/arm_scmi/notify.h | 8 ++++++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi= /notify.c index fb60fb07c0ed..3264e1062700 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -595,7 +595,13 @@ int scmi_notify(const struct scmi_handle *handle, u8 p= roto_id, u8 evt_id, if (!r_evt) return -EINVAL; =20 - 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 +758,7 @@ int scmi_register_protocol_events(const struct scmi_han= dle *handle, u8 proto_id, const struct scmi_protocol_handle *ph, const struct scmi_protocol_events *ee) { - int i; + int i, max_msg_sz, max_report_sz; unsigned int num_sources; size_t payld_sz =3D 0; struct scmi_registered_events_desc *pd; @@ -767,6 +773,9 @@ int scmi_register_protocol_events(const struct scmi_han= dle *handle, u8 proto_id, if (!ni) return -ENOMEM; =20 + max_msg_sz =3D ph->hops->get_max_msg_size(ph); + max_report_sz =3D max_msg_sz + sizeof(ktime_t); + /* num_sources cannot be <=3D 0 */ if (ee->num_sources) { num_sources =3D ee->num_sources; @@ -779,8 +788,13 @@ int scmi_register_protocol_events(const struct scmi_ha= ndle *handle, u8 proto_id, } =20 evt =3D ee->evts; - for (i =3D 0; i < ee->num_events; i++) - payld_sz =3D max_t(size_t, payld_sz, evt[i].max_payld_sz); + for (i =3D 0; i < ee->num_events; i++) { + if (evt[i].max_payld_sz =3D=3D 0) { + payld_sz =3D max_msg_sz; + break; + } + payld_sz =3D max(payld_sz, evt[i].max_payld_sz); + } payld_sz +=3D sizeof(struct scmi_event_header); =20 pd =3D scmi_allocate_registered_events_desc(ni, proto_id, ee->queue_sz, @@ -807,7 +821,8 @@ int scmi_register_protocol_events(const struct scmi_han= dle *handle, u8 proto_id, mutex_init(&r_evt->sources_mtx); =20 r_evt->report =3D devm_kzalloc(ni->handle->dev, - evt->max_report_sz, GFP_KERNEL); + evt->max_report_sz ?: max_report_sz, + GFP_KERNEL); if (!r_evt->report) return -ENOMEM; =20 diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi= /notify.h index f18f98c5ab3b..c2eb4844c7db 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 mess= age - * @max_report_sz: Max possible size for the report of a notification mess= age + * @max_payld_sz: Max possible size for the payload of a notification mess= age. + * 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 mess= age. + * Set to zero to use the maximum payload size allowed by the + * transport plus the additional ktime_t timestamp. * * Each SCMI protocol, during its initialization phase, can describe the e= vents * it wishes to support in a few struct scmi_event and pass them to the co= re --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DAF153EA94C; Sun, 20 Sep 2026 09:20:23 +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=1789896028; cv=none; b=UPD+JTwGxspNlR0TThKHIOdN8vVSowkOsC2F/kL3jM+i8WzO3DG1QxG/xyFWP4kwIP4pgEEPPoqdcZcedBhVGcIouVJftwvxo3ZoLY8HlZQbWTpNB8qxpt3SKgm4bxVk/B3ruy1oTOsVEikWTnzYFm+20k+y8fNxsuz8vQM37HM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896028; c=relaxed/simple; bh=sC1bOpzrpszySpo0zByntWzUlT0xtl8OobMjkxCvOxk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EtWr0Rjbvj/fNo1EGqOd1wIjqOhlckkhwZMxk6s0KTX5y2XpNArwWSYipz5BtHy8VWCIAzkZBhDAsVKZw1mAZ5HJqIEDAa1o9eVAz6q4A1W3vJ1CzOMx7rMKsYQUkGmff3syvyziuO9m4EmHUyhZ8X6hp41WwLkEMdZ8f7HGavA= 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=YuKimaft; 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="YuKimaft" 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 C57E41C0A; Sun, 20 Sep 2026 02:20:18 -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 0F9C33F59E; Sun, 20 Sep 2026 02:20:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896022; bh=sC1bOpzrpszySpo0zByntWzUlT0xtl8OobMjkxCvOxk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YuKimaft2/ZwIFj8AG6LOdSx6JSXM0rX2X5MkvJntoK7Z/94R2qWLOyCvWWFUziyq 6xw59aD3BOt7y3mLBRdweXdQ+JJ8gmfWhMuuSvysxoIwLUY1b4eaNTstUVXSh9/ZFy RVVFj7RrMWBPhSY91fDF25smp1U2D11f/irF/f9Y= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Cameron Subject: [PATCH v12 03/25] firmware: arm_scmi: Introduce protocol instance notifiers Date: Sun, 20 Sep 2026 10:19:06 +0100 Message-ID: <20260920091928.2014972-4-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" SCMI Protocol notifications are typically used by SCMI drivers to detect and react to particular conditions: this was the assumption and the classic usage scenario upon which the SCMI notification framework was built. Some protocols like Telemetry, though, could be interested in enabling and registering for such notifications: in such a scenario each initialized protocol instance could want to register a common protocol notifier. In order to reuse the existing SCMI Notifications machinery, allow protocols themselves to register for their own notifications and provide their own notifier callbacks. Each protocol, at the end of its initialization phase, can now provide one unique per-protocol-instance notifier block, which will be registered, by the core SCMI stack, on the related notification chain: such notifier will be automatically removed during the protocol de-initialization phase. Reviewed-by: Jonathan Cameron Signed-off-by: Cristian Marussi --- v10 -->v11 - added a few dev_dbg v8 --> v9 - Added membarrier comment v7 --> v8 - Reworked logic around protocol notifier registration to address possible races as pointed out by Sashiko - Reviewed commit message v5 -->v6 - move protocol notifier registration out of proto_init v2-->v3 - split out unrelated changes on event sizing v1-->v2 - Fixed multiline comment format --- drivers/firmware/arm_scmi/common.h | 22 +++++++ drivers/firmware/arm_scmi/driver.c | 89 +++++++++++++++++++++------ drivers/firmware/arm_scmi/notify.c | 40 ++++++++++++ drivers/firmware/arm_scmi/protocols.h | 10 +++ 4 files changed, 142 insertions(+), 19 deletions(-) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi= /common.h index 6f89ba96493d..1d9e6af09672 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -687,8 +688,29 @@ static struct platform_driver __drv =3D { \ .probe =3D __tag##_probe, \ } =20 +struct scmi_protocol_notifier { + u8 proto_id; + u8 evt_id; + const u32 *src_id; + struct notifier_block *nb; + bool registered; +}; + 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_protocol_notifier_register(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno); +int scmi_protocol_notifier_unregister(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno); + +static inline bool +scmi_protocol_notifier_registered(struct scmi_protocol_notifier *pno) +{ + /* Ensure registered is visible */ + return smp_load_acquire(&pno->registered); +} + 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 922777e86d58..26f39edc770a 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -91,6 +91,8 @@ struct scmi_xfers_info { * @gid: A reference for per-protocol devres management. * @users: A refcount to track effective users of this protocol. * @priv: Reference for optional protocol private data. + * @pno: A protocol instance notifier descriptor: active only when the + * included notifier block @nb is non-NULL. * @version: Protocol version supported by the platform as detected at run= time. * @negotiated_version: When the platform supports a newer protocol versio= n, * the agent will try to negotiate with the platform the @@ -110,6 +112,7 @@ struct scmi_protocol_instance { void *gid; refcount_t users; void *priv; + struct scmi_protocol_notifier pno; unsigned int version; unsigned int negotiated_version; struct scmi_protocol_handle ph; @@ -1656,6 +1659,24 @@ static void *scmi_get_protocol_priv(const struct scm= i_protocol_handle *ph) return pi->priv; } =20 +static int +scmi_register_instance_notifier(const struct scmi_protocol_handle *ph, u8 = evt_id, + const u32 *src_id, struct notifier_block *nb) +{ + struct scmi_protocol_instance *pi =3D ph_to_pi(ph); + + pi->pno.proto_id =3D pi->proto->id; + pi->pno.evt_id =3D evt_id; + pi->pno.src_id =3D src_id; + /* + * Ensure the descriptor fields are visibile when the notifier block is + * made available + */ + smp_store_release(&pi->pno.nb, nb); + + return 0; +} + static const struct scmi_xfer_ops xfer_ops =3D { .xfer_get_init =3D xfer_get_init, .reset_rx_to_maxsz =3D reset_rx_to_maxsz, @@ -2256,6 +2277,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *i= nfo, pi->ph.hops =3D &helpers_ops; pi->ph.set_priv =3D scmi_set_protocol_priv; pi->ph.get_priv =3D scmi_get_protocol_priv; + pi->ph.instance_notifier_register =3D scmi_register_instance_notifier; refcount_set(&pi->users, 1); =20 /* @@ -2318,24 +2340,34 @@ static struct scmi_protocol_instance * __must_check scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_i= d) { struct scmi_protocol_instance *pi; + struct notifier_block *proto_notifier_nb =3D NULL; struct scmi_info *info =3D handle_to_scmi_info(handle); =20 - mutex_lock(&info->protocols_mtx); - pi =3D idr_find(&info->protocols, protocol_id); + scoped_guard(mutex, &info->protocols_mtx) { + pi =3D idr_find(&info->protocols, protocol_id); + if (pi) { + refcount_inc(&pi->users); + } else { + const struct scmi_protocol *proto; =20 - if (pi) { - refcount_inc(&pi->users); - } else { - const struct scmi_protocol *proto; + /* Fails if protocol not registered on bus */ + proto =3D scmi_protocol_get(protocol_id, &info->version); + if (!proto) + return ERR_PTR(-EPROBE_DEFER); =20 - /* Fails if protocol not registered on bus */ - proto =3D scmi_protocol_get(protocol_id, &info->version); - if (proto) pi =3D scmi_alloc_init_protocol_instance(info, proto); - else - pi =3D ERR_PTR(-EPROBE_DEFER); + if (IS_ERR(pi)) + return pi; + + proto_notifier_nb =3D READ_ONCE(pi->pno.nb); + } + } + + if (proto_notifier_nb) { + if (scmi_protocol_notifier_register(pi->handle, &pi->pno)) + dev_warn(handle->dev, + "Failed to register protocol notifier\n"); } - mutex_unlock(&info->protocols_mtx); =20 return pi; } @@ -2366,13 +2398,35 @@ int scmi_protocol_acquire(const struct scmi_handle = *handle, u8 protocol_id) void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_i= d) { struct scmi_info *info =3D handle_to_scmi_info(handle); + struct notifier_block *proto_notifier_nb =3D NULL; struct scmi_protocol_instance *pi; =20 - mutex_lock(&info->protocols_mtx); - pi =3D idr_find(&info->protocols, protocol_id); - if (WARN_ON(!pi)) - goto out; + scoped_guard(mutex, &info->protocols_mtx) { + pi =3D idr_find(&info->protocols, protocol_id); + if (WARN_ON(!pi)) + return; =20 + /* + * If a protocol notifier was registered and this is the + * last istance releasing the protocol, mark the notifier + * for un-registration: note that the notifier itself counts + * as one user, as for any other regular notification, so if a + * protocol notifier is registered and there are only 2 users + * active we can derive that this is the last protocol + * instance de-registering. + */ + if (scmi_protocol_notifier_registered(&pi->pno) && + refcount_read(&pi->users) =3D=3D 2) + proto_notifier_nb =3D READ_ONCE(pi->pno.nb); + } + + if (proto_notifier_nb) { + if (scmi_protocol_notifier_unregister(pi->handle, &pi->pno)) + dev_err(handle->dev, + "Failed to release protocol notifier\n"); + } + + guard(mutex)(&info->protocols_mtx); if (refcount_dec_and_test(&pi->users)) { void *gid =3D pi->gid; =20 @@ -2390,9 +2444,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); } =20 void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi= /notify.c index 3264e1062700..188282d9adc0 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1417,6 +1417,25 @@ static int scmi_notifier_register(const struct scmi_= handle *handle, return ret; } =20 +int scmi_protocol_notifier_register(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno) +{ + int ret; + + ret =3D scmi_notifier_register(handle, pno->proto_id, pno->evt_id, + pno->src_id, pno->nb); + if (ret) + return ret; + + /* Make sure registration is visible */ + smp_store_release(&pno->registered, true); + + dev_dbg(handle->dev, + "Registered SCMI protocol [0x%X] notifier\n", pno->proto_id); + + return 0; +} + /** * scmi_notifier_unregister() - Unregister a notifier_block for an event * @handle: The handle identifying the platform instance against which the @@ -1474,6 +1493,27 @@ static int scmi_notifier_unregister(const struct scm= i_handle *handle, return 0; } =20 +int scmi_protocol_notifier_unregister(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno) +{ + int ret; + + /* Make sure de-registration is visible BEFORE calling unregister */ + smp_store_release(&pno->registered, false); + ret =3D scmi_notifier_unregister(handle, pno->proto_id, pno->evt_id, + pno->src_id, pno->nb); + if (WARN_ON(ret)) { + /* Make sure rollback is visible */ + smp_store_release(&pno->registered, true); + return ret; + } + + dev_dbg(handle->dev, + "Unregistered SCMI protocol [0x%X] notifier\n", pno->proto_id); + + return 0; +} + struct scmi_notifier_devres { const struct scmi_handle *handle; u8 proto_id; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_s= cmi/protocols.h index 15ad5162e37a..b7f29003a7dc 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -166,6 +166,13 @@ struct scmi_proto_helpers_ops; * can be used by the protocol implementation to generate SCMI messages. * @set_priv: A method to set protocol private data for this instance. * @get_priv: A method to get protocol private data previously set. + * @instance_notifier_register: A method to register interest for specific + * notifications from within a protocol + * implementation unit: ONLY one instance notifier + * can be registered per-protocol. + * The related notifier block will be registered + * right after the protocol is initialized and + * automatically removed on protocol release. * * This structure represents a protocol initialized against specific SCMI * instance and it will be used as follows: @@ -185,6 +192,9 @@ struct scmi_protocol_handle { const struct scmi_proto_helpers_ops *hops; int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); void *(*get_priv)(const struct scmi_protocol_handle *ph); + int (*instance_notifier_register)(const struct scmi_protocol_handle *ph, + u8 evt_id, const u32 *src_id, + struct notifier_block *nb); }; =20 /** --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 17D8F3CB56F; Sun, 20 Sep 2026 09:20:28 +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=1789896033; cv=none; b=B7vrUaQhEvQazEODCEzcFp/2wL8ImgQVqwsGG+RkZpULfogT1hjnrGA0+EXKfAE+35ztbcWZdxjq21ix9wbmt5wNfPQH7mCfP4+QF7hYHyN+zI8EY/muTzut5ih/jXsjJP7KYnYWCFdRPRhABCXKCGAJLaO/eIo6jxvRjOgGwBE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896033; c=relaxed/simple; bh=hfA0ap4Xo70CAlarCd2qZoILhqj61SvsIL72n1hY+fo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=m5W4H+MkO2f2uKAwyCl8K/PHKk5p11oFJOuApvX+XZlyT4n7K8soZbajcE2iwcuzf1aNzcHg8FGPwJomzdK8RqB/n26zAJ2uLzGRcC0XExftFG4B7YOjuZpeiGDzJ7ED1nTS0b+72/miWPzq/HUztPiTnvkJVApAh4AEXz5efP8= 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=Kk/ILmTr; 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="Kk/ILmTr" 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 A17AE1691; Sun, 20 Sep 2026 02:20:23 -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 A9E693F59E; Sun, 20 Sep 2026 02:20:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896027; bh=hfA0ap4Xo70CAlarCd2qZoILhqj61SvsIL72n1hY+fo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Kk/ILmTr7eRXlYE8E8T6l3A/i/G6qDU/c8FNSoskkY7oNrf2OD5NC2NVFBKtA/5EW gkh74/p49uzp+KQizvvUmxvVHySLyr3m48jbAeiQhbZzR+52REEJLcFTT4xPqVDJqj uTs9b6nxVplhCHZVZN9F+IDydmteJZQVlvmxECvs= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , "Rob Herring (Arm)" , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v12 04/25] dt-bindings: firmware: arm,scmi: Add support for telemetry protocol Date: Sun, 20 Sep 2026 10:19:07 +0100 Message-ID: <20260920091928.2014972-5-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 new SCMI v4.0 Telemetry protocol bindings definitions. Acked-by: Rob Herring (Arm) Signed-off-by: Cristian Marussi --- v3 --> v4 - changed protocol number to lowercase 1b - fixed misplaced block for protocol 0x1b Cc: Rob Herring Cc: Krzysztof Kozlowski Cc: Conor Dooley Cc: devicetree@vger.kernel.org --- Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Doc= umentation/devicetree/bindings/firmware/arm,scmi.yaml index 6fc44105f1c3..b63543390fd9 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -324,6 +324,14 @@ properties: required: - reg =20 + protocol@1b: + $ref: '#/$defs/protocol-node' + unevaluatedProperties: false + + properties: + reg: + const: 0x1b + unevaluatedProperties: false =20 $defs: --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 51F983E00A3; Sun, 20 Sep 2026 09:20:33 +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=1789896037; cv=none; b=S7c6Tmsz2jGGBakhKtBKin4lDcQ7zBs+QrfoO07GWiS9jqr0SLnq/ESrthsxfNz0KtDFRoLcqYhQpGJx8dtfWhpJuXkD9+bwYH4o7VbJjs4CQfAWPBP8ne4ivq7LyejVuIjY9JAavIrjJ7L4f7ViG9I9BD+GArlJ/Yp86W8ZeaQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896037; c=relaxed/simple; bh=5P9ZuvQ/38HQaH48tpNZsAjPLPf474W8M/GUNsIkNhg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SU+8cd+uIa/mAyopltYSHvM6vX8wYZmscMhdtZHaUaeAxkYLTtsr4bb6mRYwBBtJvyyciwJ+RBI6I8gKWJ5pWdmqxRF9E51YHcm17z32Ao7hw4yHvdAl24u7wc/KYksx7m87CW9CS3M6jsdqsOff4JA7flrL2N+DykJDe5Okxts= 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=LOoksWJS; 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="LOoksWJS" 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 EAB3D175A; Sun, 20 Sep 2026 02:20:27 -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 8607C3F59E; Sun, 20 Sep 2026 02:20:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896031; bh=5P9ZuvQ/38HQaH48tpNZsAjPLPf474W8M/GUNsIkNhg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LOoksWJSjtMeePyMiXJfH+9OLN2gtTHLTPlgC/C2JRt8NQlYae9Tn+DHd45zWfCnf A8vnrnjmMctFuhsd/6bbVGkjYtmRtKtgdgKz+zMy7atW/8B+6JOEOS3cia/2P/lBLI Xj7jnhlnAEcctgcTvoI4LGYDm5sxRyd34Nft0CxY= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 05/25] include: trace: Add Telemetry trace events Date: Sun, 20 Sep 2026 10:19:08 +0100 Message-ID: <20260920091928.2014972-6-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 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 --- v7 --> v8 - use proper FTRACE helpers for variable strings --- include/trace/events/scmi.h | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h index 703b7bb68e44..002d2bc9ae15 100644 --- a/include/trace/events/scmi.h +++ b/include/trace/events/scmi.h @@ -180,6 +180,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) + __string(tag, tag) + __field(u64, startm) + __field(u64, endm) + ), + + TP_fast_assign( + __entry->de_id =3D de_id; + __assign_str(tag); + __entry->startm =3D startm; + __entry->endm =3D endm; + ), + + TP_printk("[%s] de_id=3D0x%llX - startm=3D%016llX endm=3D%016llX", + __get_str(tag), __entry->de_id, __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) + __string(tag, tag) + ), + + TP_fast_assign( + __entry->ts =3D ts; + __entry->de_id =3D de_id; + __entry->value =3D value; + __assign_str(tag); + ), + + TP_printk("[%s] ts=3D%llu de_id=3D0x%04llX value=3D%016llu", + __get_str(tag), __entry->ts, __entry->de_id, __entry->value) +); + #endif /* _TRACE_SCMI_H */ =20 /* This part must be outside protection */ --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 860F73ED5DA; Sun, 20 Sep 2026 09:20:37 +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=1789896044; cv=none; b=pMp6DxMt1U7UGZWuhfKi1O/4m4qQ0NUlssFSk2NCIO8s22EQzh5OQu3+4dQmpjEvH4Mb9bX08pzd3GYdBrwuDOwddFn219wzqKFIqQZhCD5dfxkgnZvgUluEYwYECoq2AEYPmGW7rHag6YrrJsOw9XzKlvsK0i0U32OvkfbRvU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896044; c=relaxed/simple; bh=S3wQpoZUwFYUZ739zxCuo/ItdYCefYYEBP1w5HY9FUA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cmPuPWDfPBybgX9YBn1iIXEMj/bOEIX4wNQI2rTsqFf/DuIA2jgxuK9MuZLVl9l7EvR5TD8OsaXYQjZDGi0CwFyiw6YRj6OwjGU9I8WxfljrP64rN6++6yt+/8UVWcjCuwAEY7cYoCotZj+ObiObPZoSenKTiJq2WjtzXkf56Bo= 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=F78tz0KY; 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="F78tz0KY" 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 5953A1C0A; Sun, 20 Sep 2026 02:20:32 -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 CCCDE3F59E; Sun, 20 Sep 2026 02:20:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896035; bh=S3wQpoZUwFYUZ739zxCuo/ItdYCefYYEBP1w5HY9FUA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F78tz0KYUGzIb70ODISyNMKmaxFlFLo5abCbVGojAtAS80DAKAkv8L9yotYyTWENb PIKgBcrc3MMqeCJAbqyqeszu7zWtoHZ+lFqUyfjDmJCMOrPWr4KrgEYp/3k4Nvqe48 xHtJxaef2OuQviecY6SC8VdK0s5dt9iOx9Odvcks= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 06/25] firmware: arm_scmi: Add basic Telemetry support Date: Sun, 20 Sep 2026 10:19:09 +0100 Message-ID: <20260920091928.2014972-7-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 SCMIv4.0 Telemetry basic support to enable initialization and resources enumeration: add all the telemetry messages definitions and parsing logic but only a few simple state gathering protocol operations. Signed-off-by: Cristian Marussi --- v11 --> v12 - dropped counted-by in smhtis - reworked barriers around res_get - added smp_store_release on TDE registration v10 -->v11 - relocated a few parsing macros from another patch - cleanup exit path on resource enumeration - remove 'naked' ti->rinfo accesses - walk xa_des to free DEs on removal v8 --> v9 - Fixes for some Sashiko issues on v8 - use counted_by_lle where appropriate - consider header in boudanry check in DE_DESCRIPTION iterators - bail out if SHMTI areas are NOT 32bit aligned - warn if SHMTI areas are NOT 64bit aligned - convert SCMI_TDE_VAR_SZ macro to an inline function - make some enum definition explicit v7 --> v8 - Fixes for some Sashiko issues on v7 - add proper msg boundary parsing checks on DE_DESCRIPTION multipart - add proper cleanup/rollback on groups_init err_path - update num_des_tstamp ONLY after a DE is successfully initialized - proper cleanup tde during tde_put (preserving info/mutex bits) - add membarriers in enumerate resources - add a check and a warn on SHMTI phys_addr alignment - reword a few dev_errs - use tde_free_put on resoucers_free v6 --> v7 - use uuid_t for proper Telemetry UUID DE_IMPL_VERSION endianity v5 --> v6 - define uuid_line dwords as BE - fixed Sashiko reviews where applicable: https://sashiko.dev/#/message/20260703123601.381275-8-cristian.marussi%4= 0arm.com - fix missing le32 and bound check in variable payload parsing - fix TDE duplicatin logic - fix intervals cleanup - fixes also KMEMLEAK report - implement proper freeinfg for group resources - proper SHMTI iterations - removed useless/stale static in scmi_telemetry_tde_get() v4 --> v5 - fix de->grp_id initialization for DEs not belonging to a group - removed UAPI structures dependency - expose discovered SHMTIs areas in scmi_protocol.h - fixes sparse warnings v3 -->v4 - bail-out on FW_BUG errors - count timestamp capable DE to track enables v2 --> v3 - split from monolithic Telemetry patch - fix checkpatch macros complaints - fix ACCESS_PRIVATE usage - add a few comments on allocation/enumeration lifetime - use interval.num_intervals - removed needless cleanup handler usage - simply return from scmi_telemetry_de_lookup() - fixed composing_des name length to 08X --- 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 | 1547 +++++++++++++++++++++++++ include/linux/scmi_protocol.h | 183 ++- 5 files changed, 1733 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) +=3D raw_mo= de.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) =3D shmem.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) +=3D msg.o scmi-protocols-y :=3D base.o clock.o perf.o power.o reset.o sensors.o syst= em.o voltage.o powercap.o -scmi-protocols-y +=3D pinctrl.o +scmi-protocols-y +=3D pinctrl.o telemetry.o scmi-module-objs :=3D $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transpor= t-y) =20 obj-$(CONFIG_ARM_SCMI_PROTOCOL) +=3D transports/ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi= /driver.c index 26f39edc770a..1b23d533e7e0 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3604,6 +3604,7 @@ static int __init scmi_driver_init(void) scmi_system_register(); scmi_powercap_register(); scmi_pinctrl_register(); + scmi_telemetry_register(); =20 return platform_driver_register(&scmi_driver); } @@ -3622,6 +3623,7 @@ static void __exit scmi_driver_exit(void) scmi_system_unregister(); scmi_powercap_unregister(); scmi_pinctrl_unregister(); + scmi_telemetry_unregister(); =20 platform_driver_unregister(&scmi_driver); =20 diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_s= cmi/protocols.h index b7f29003a7dc..1f9925b8f495 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -401,5 +401,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); =20 #endif /* _SCMI_PROTOCOLS_H */ diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c new file mode 100644 index 000000000000..d8176a7d78b8 --- /dev/null +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -0,0 +1,1547 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Telemetry Protocol + * + * Copyright (C) 2026 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "protocols.h" +#include "notify.h" + +#include + +/* Updated only after ALL the mandatory features for that version are merg= ed */ +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 + +#define SCMI_TLM_TDCF_MAX_RETRIES 5 + +#define SCMI_TLM_DE_IMPL_NUM_DWORDS 4 + +enum scmi_telemetry_protocol_cmd { + TELEMETRY_LIST_SHMTI =3D 0x3, + TELEMETRY_DE_DESCRIPTION =3D 0x4, + TELEMETRY_LIST_UPDATE_INTERVALS =3D 0x5, + TELEMETRY_DE_CONFIGURE =3D 0x6, + TELEMETRY_DE_ENABLED_LIST =3D 0x7, + TELEMETRY_CONFIG_SET =3D 0x8, + TELEMETRY_READING_COMPLETE =3D TELEMETRY_CONFIG_SET, + TELEMETRY_CONFIG_GET =3D 0x9, + TELEMETRY_RESET =3D 0xA, +}; + +struct scmi_msg_resp_telemetry_protocol_attributes { + __le32 de_num; + __le32 groups_num; + __le32 de_implem_rev_dword[SCMI_TLM_DE_IMPL_NUM_DWORDS]; + __le32 attributes; +#define SUPPORTS_SINGLE_READ(x) (le32_get_bits((x), BIT(31))) +#define SUPPORTS_CONTINUOS_UPDATE(x) (le32_get_bits((x), BIT(30))) +#define SUPPORTS_PER_GROUP_CONFIG(x) (le32_get_bits((x), BIT(18))) +#define SUPPORTS_RESET(x) (le32_get_bits((x), BIT(17))) +#define SUPPORTS_FC(x) (le32_get_bits((x), BIT(16))) + __le32 default_blk_ts_rate; +}; + +struct scmi_telemetry_update_notify_payld { + __le32 agent_id; + __le32 status; + __le32 num_dwords; + __le32 array[] __counted_by_le(num_dwords); +}; + +struct scmi_shmti_desc { + __le32 id; + __le32 addr_low; + __le32 addr_high; + __le32 length; + __le32 flags; +}; + +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) (le32_get_bits((d)->attr_1, BIT(31))) +#define IS_FC_SUPPORTED(d) (le32_get_bits((d)->attr_1, BIT(30))) +#define GET_DE_TYPE(d) (le32_get_bits((d)->attr_1, GENMASK(29, 22))) +#define IS_PERSISTENT(d) (le32_get_bits((d)->attr_1, BIT(21))) +#define GET_DE_UNIT_EXP(d) \ + ({ \ + __u32 __signed_exp =3D \ + le32_get_bits((d)->attr_1, GENMASK(20, 13)); \ + \ + sign_extend32(__signed_exp, 7); \ + }) +#define GET_DE_UNIT(d) (le32_get_bits((d)->attr_1, GENMASK(12, 5))) +#define TSTAMP_SUPPORT(d) (le32_get_bits((d)->attr_1, GENMASK(1, 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) (le32_get_bits((x), BIT(12)) =3D=3D 0) + __le32 intervals[]; +}; + +struct scmi_msg_telemetry_de_enabled_list { + __le32 index; + __le32 flags; +}; + +struct scmi_enabled_de_desc { + __le32 id; + __le32 mode; +}; + +struct scmi_msg_resp_telemetry_de_enabled_list { + __le32 flags; + struct scmi_enabled_de_desc entry[]; +}; + +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 SHMTI_ID_INVALID GENMASK(31, 0) +#define IS_SHMTI_ID_VALID(x) ((x) !=3D SHMTI_ID_INVALID) + __le32 shmti_de_offset; + __le32 blk_ts_offset; +}; + +struct scmi_msg_telemetry_config_set { + __le32 grp_id; + __le32 control; +#define TELEMETRY_ENABLE (BIT(0)) + +#define TELEMETRY_MODE_SET(x) (FIELD_PREP(GENMASK(4, 1), (x))) +#define TLM_ONDEMAND (0) +#define TLM_NOTIFS (1) +#define TLM_SINGLE (2) +#define TELEMETRY_MODE_ONDEMAND TELEMETRY_MODE_SET(TLM_ONDEMAND) +#define TELEMETRY_MODE_NOTIFS TELEMETRY_MODE_SET(TLM_NOTIFS) +#define TELEMETRY_MODE_SINGLE TELEMETRY_MODE_SET(TLM_SINGLE) + +#define TLM_ORPHANS (0) +#define TLM_GROUP (1) +#define TLM_ALL (2) +#define TELEMETRY_SET_SELECTOR(x) (FIELD_PREP(GENMASK(8, 5), (x))) +#define TELEMETRY_SET_SELECTOR_ORPHANS TELEMETRY_SET_SELECTOR(TLM_ORPHANS) +#define TELEMETRY_SET_SELECTOR_GROUP TELEMETRY_SET_SELECTOR(TLM_GROUP) +#define TELEMETRY_SET_SELECTOR_ALL TELEMETRY_SET_SELECTOR(TLM_ALL) + __le32 sampling_rate; +}; + +struct scmi_msg_resp_telemetry_reading_complete { + __le32 num_dwords; + __le32 dwords[] __counted_by_le(num_dwords); +}; + +struct scmi_msg_telemetry_config_get { + __le32 grp_id; + __le32 flags; +#define TELEMETRY_GET_SELECTOR(x) (FIELD_PREP(GENMASK(3, 0), (x))) +#define TELEMETRY_GET_SELECTOR_ORPHANS TELEMETRY_GET_SELECTOR(TLM_ORPHANS) +#define TELEMETRY_GET_SELECTOR_GROUP TELEMETRY_GET_SELECTOR(TLM_GROUP) +#define TELEMETRY_GET_SELECTOR_ALL TELEMETRY_GET_SELECTOR(TLM_ALL) +}; + +struct scmi_msg_resp_telemetry_config_get { + __le32 control; + __le32 sampling_rate; +}; + +/* TDCF */ + +#define _I(__a) (ioread32((void __iomem *)(__a))) + +#define TO_CPU_64(h, l) ((((u64)(h)) << 32) | (l)) + +/* + * Define the behaviour of a SHMTI scan defining what information will + * be gathered and which Telemetry items can be updated. + */ +enum scan_mode { + SCAN_LOOKUP, /* Update only value/tstamp */ + SCAN_UPDATE, /* Update also location offset */ + SCAN_DISCOVERY /* Update xa_des: allows for new DEs to be discovered */ +}; + +struct fc_line { + __le32 data_low; + __le32 data_high; +}; + +struct fc_tsline { + __le32 data_low; + __le32 data_high; + __le32 ts_low; + __le32 ts_high; +}; + +struct line { + __le32 data_low; + __le32 data_high; +}; + +struct blk_tsline { + __le32 ts_low; + __le32 ts_high; +}; + +struct tsline { + __le32 data_low; + __le32 data_high; + __le32 ts_low; + __le32 ts_high; +}; + +struct uuid_line { + __be32 dwords[SCMI_TLM_DE_IMPL_NUM_DWORDS]; +}; + +enum tdcf_line_types { + TDCF_DATA_LINE =3D 0, + TDCF_BLK_TS_LINE =3D 1, + TDCF_UUID_LINE =3D 2, +}; + +struct payload { + __le32 meta; +#define PAYLD_META(x) (_I(&((x)->meta))) +#define LINE_TYPE_RAW(m) (FIELD_GET(GENMASK(7, 4), (m))) +#define LINE_TYPE(x) (LINE_TYPE_RAW(PAYLD_META(x))) +#define IS_DATA_LINE(x) (LINE_TYPE(x) =3D=3D TDCF_DATA_LINE) +#define IS_BLK_TS_LINE(x) (LINE_TYPE(x) =3D=3D TDCF_BLK_TS_LINE) +#define IS_UUID_LINE(x) (LINE_TYPE(x) =3D=3D TDCF_UUID_LINE) +#define USE_BLK_TS_RAW(m) ((m) & BIT(3)) +#define USE_BLK_TS(x) (USE_BLK_TS_RAW(PAYLD_META(x))) +#define HAS_LINE_EXT_RAW(m) ((m) & BIT(2)) +#define HAS_LINE_EXT(x) (HAS_LINE_EXT_RAW(PAYLD_META(x))) +#define LINE_TS_VALID_RAW(m) ((m) & BIT(1)) +#define LINE_TS_VALID(x) (LINE_TS_VALID_RAW(PAYLD_META(x))) +#define DATA_INVALID_RAW(m) ((m) & BIT(0)) +#define DATA_INVALID(x) (DATA_INVALID_RAW(PAYLD_META(x))) +#define BLK_TS_INVALID(p) \ +({ \ + u32 _m =3D PAYLD_META(p); \ + bool invalid; \ + \ + invalid =3D LINE_TS_VALID_RAW(_m) || HAS_LINE_EXT_RAW(_m) || \ + USE_BLK_TS_RAW(_m) || DATA_INVALID_RAW(_m); \ + invalid; \ +}) + +#define UUID_INVALID(p) \ +({ \ + u32 _m =3D PAYLD_META(p); \ + bool invalid; \ + \ + invalid =3D LINE_TS_VALID_RAW(_m) || USE_BLK_TS_RAW(_m) || \ + DATA_INVALID_RAW(_m) || !HAS_LINE_EXT_RAW(_m); \ + invalid; \ +}) + __le32 id; + union { + struct line l; + struct tsline tsl; + struct blk_tsline blk_tsl; + struct uuid_line uuid_l; + }; +}; + +#define PAYLD_ID(x) (_I(&(((struct payload __iomem *)(x))->id))) + +#define LINE_DATA_PAYLD_WORDS \ + ((sizeof(u32) + sizeof(u32) + sizeof(struct line)) / sizeof(u32)) +#define EXT_LINE_DATA_PAYLD_WORDS \ + ((sizeof(u32) + sizeof(u32) + sizeof(struct tsline)) / sizeof(u32)) + +#define LINE_LENGTH_WORDS_RAW(m) \ + (HAS_LINE_EXT_RAW((m)) ? EXT_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORD= S) + +#define LINE_LENGTH_WORDS(x) LINE_LENGTH_WORDS_RAW(PAYLD_META(x)) + +#define LINE_LENGTH_QWORDS(x) ((LINE_LENGTH_WORDS(x)) / 2) + +struct prlg { + u32 sign_start; +#define SIGNATURE_START 0x5442474E /* TBGN */ + u32 match_start; + u32 num_qwords; + u32 hdr_meta_1; +#define TDCF_REVISION_GET(x) (le32_get_bits((x)->hdr_meta_1, GENMASK(7, 0)= )) +}; + +struct eplg { + u32 match_end; + u32 sign_end; +#define SIGNATURE_END 0x54454E44 /* TEND */ +}; + +#define TDCF_EPLG_SZ (sizeof(struct eplg)) + +struct tdcf { + struct prlg prlg; + unsigned char payld[]; +}; + +#define QWORDS(_t) (_I(&(_t)->prlg.num_qwords)) + +#define SHMTI_MIN_SIZE (sizeof(struct tdcf) + TDCF_EPLG_SZ) + +#define TDCF_START_SIGNATURE(x) (_I(&((x)->prlg.sign_start))) +#define TDCF_START_SEQ_GET(x) (_I(&((x)->prlg.match_start))) +#define IS_BAD_START_SEQ(s) ((s) & 0x1) + +#define TDCF_END_SEQ_GET(e) (_I(&((e)->match_end))) +#define TDCF_END_SIGNATURE(e) (_I(&((e)->sign_end))) +#define TDCF_BAD_END_SEQ GENMASK(31, 0) + +struct telemetry_shmti { + u32 flags; + void __iomem *base; + u32 last_magic; + struct scmi_telemetry_shmti_info info; +}; + +#define SHMTI_EPLG(s) \ + ({ \ + struct telemetry_shmti *_s =3D (s); \ + struct eplg __iomem *_eplg; \ + \ + _eplg =3D _s->base + _s->info.len - TDCF_EPLG_SZ; \ + (_eplg); \ + }) + +struct telemetry_info; + +struct telemetry_line { + enum tdcf_line_types type; + u32 last_magic; + struct payload __iomem *payld; + refcount_t users; + struct telemetry_info *ti; + /* Protect line accesses */ + struct mutex mtx; +}; + +struct telemetry_block_ts { + u64 last_ts; + u32 last_rate; + struct telemetry_line line; +}; + +#define to_blkts(l) container_of(l, struct telemetry_block_ts, line) + +struct telemetry_uuid { + uuid_t uuid; + struct telemetry_line line; +}; + +#define to_uuid(l) container_of(l, struct telemetry_uuid, line) + +enum timestamps { + TSTAMP_NONE =3D 0, + TSTAMP_LINE =3D 1, + TSTAMP_BLK =3D 2, +}; + +struct telemetry_de { + enum timestamps ts_type; + u32 ts_rate; + bool enumerated; + bool cached_msg; + void __iomem *base; + struct eplg __iomem *eplg; + u32 sid; + u32 offset; + /* NOTE THAT DE data_sz is registered in scmi_telemetry_de */ + u32 fc_size; + u64 last_val; + u64 last_ts; + u32 last_magic; + struct list_head item; + struct telemetry_block_ts *bts; + struct telemetry_uuid *uuid; + struct scmi_telemetry_de de; + /* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */ + struct mutex mtx; +}; + +#define to_tde(d) container_of(d, struct telemetry_de, de) + +#define DE_ENABLED_WITH_TSTAMP 2 + +struct telemetry_info { + bool streaming_mode; + unsigned int num_shmti; + unsigned int num_des_tstamp; + unsigned int default_blk_ts_rate; + const struct scmi_protocol_handle *ph; + struct telemetry_shmti *shmti; + struct telemetry_de *tdes; + struct scmi_telemetry_group *grps; + struct xarray xa_des; + /* Mutex to protect access to @free_des */ + struct mutex free_mtx; + struct list_head free_des; + struct list_head fcs_des; + struct scmi_telemetry_info info; + atomic_t rinfo_initializing; + struct completion rinfo_initdone; + struct scmi_telemetry_res_info *rinfo; + struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); +}; + +static struct scmi_telemetry_res_info * +__scmi_telemetry_resources_get(struct telemetry_info *ti); + +static struct telemetry_de * +scmi_telemetry_free_tde_get(struct telemetry_info *ti) +{ + struct telemetry_de *tde; + + guard(mutex)(&ti->free_mtx); + + tde =3D list_first_entry_or_null(&ti->free_des, struct telemetry_de, item= ); + if (!tde) + return tde; + + list_del(&tde->item); + + return tde; +} + +static void scmi_telemetry_free_tde_put(struct telemetry_info *ti, + struct telemetry_de *tde) +{ + struct scmi_telemetry_de_info *info; + + guard(mutex)(&ti->free_mtx); + /* Save clear and restore */ + info =3D READ_ONCE(tde->de.info); + memset(info, 0, sizeof(*info)); + memset(tde, 0, offsetof(struct telemetry_de, mtx)); + tde->de.info =3D info; + list_add_tail(&tde->item, &ti->free_des); +} + +static struct telemetry_de *scmi_telemetry_tde_lookup(struct telemetry_inf= o *ti, + unsigned int de_id) +{ + struct scmi_telemetry_de *de; + + de =3D xa_load(&ti->xa_des, de_id); + if (!de) + return NULL; + + return to_tde(de); +} + +static struct telemetry_de *scmi_telemetry_tde_get(struct telemetry_info *= ti, + unsigned int de_id) +{ + struct telemetry_de *tde; + + /* Pick a new tde */ + tde =3D scmi_telemetry_free_tde_get(ti); + if (!tde) { + dev_err(ti->ph->dev, "Cannot get TDE for ID:0x%08X\n", de_id); + return ERR_PTR(-ENOSPC); + } + + tde->sid =3D SHMTI_ID_INVALID; + + return tde; +} + +static int scmi_telemetry_tde_register(struct telemetry_info *ti, + struct telemetry_de *tde) +{ + struct scmi_telemetry_res_info *rinfo; + int ret; + + /* Get rinfo without triggering a recursive enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + if (rinfo->num_des >=3D ti->info.base.num_des) { + ret =3D -ENOSPC; + goto err; + } + + /* Store DE pointer by de_id ... */ + ret =3D xa_insert(&ti->xa_des, tde->de.info->id, &tde->de, GFP_KERNEL); + if (ret) + goto err; + + /* ... and in the general array */ + rinfo->des[rinfo->num_des] =3D &tde->de; + /* Make sure the freshly registered DE is visible before the index update= */ + smp_store_release(&rinfo->num_des, rinfo->num_des + 1); + + return 0; + +err: + dev_err(ti->ph->dev, "Cannot register TDE for ID:0x%08X\n", + tde->de.info->id); + + return ret; +} + +struct scmi_tlm_de_priv { + struct telemetry_info *ti; + void *next; + size_t rx_len; +}; + +static int +scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) +{ + struct scmi_msg_resp_telemetry_protocol_attributes *resp; + const struct scmi_protocol_handle *ph =3D ti->ph; + struct scmi_xfer *t; + int ret; + + ret =3D ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, + sizeof(*resp), &t); + if (ret) + return ret; + + resp =3D t->rx.buf; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + __le32 attr =3D resp->attributes; + __be32 de_impl_version[SCMI_TLM_DE_IMPL_NUM_DWORDS]; + + ti->info.base.num_des =3D le32_to_cpu(resp->de_num); + ti->info.base.num_groups =3D le32_to_cpu(resp->groups_num); + for (int i =3D 0; i < SCMI_TLM_DE_IMPL_NUM_DWORDS; i++) + de_impl_version[i] =3D + cpu_to_be32(le32_to_cpu(resp->de_implem_rev_dword[i])); + import_uuid(&ti->info.base.primary_revision, (const __u8 *)&de_impl_vers= ion); + ti->info.single_read_support =3D SUPPORTS_SINGLE_READ(attr); + ti->info.continuos_update_support =3D SUPPORTS_CONTINUOS_UPDATE(attr); + ti->info.per_group_config_support =3D SUPPORTS_PER_GROUP_CONFIG(attr); + ti->info.reset_support =3D SUPPORTS_RESET(attr); + ti->info.fc_support =3D SUPPORTS_FC(attr); + ti->num_shmti =3D le32_get_bits(attr, GENMASK(15, 0)); + ti->default_blk_ts_rate =3D le32_to_cpu(resp->default_blk_ts_rate); + } + + 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 =3D response; + struct scmi_tlm_de_priv *p =3D priv; + + st->num_returned =3D le32_get_bits(r->num_desc, GENMASK(15, 0)); + st->num_remaining =3D le32_get_bits(r->num_desc, GENMASK(31, 16)); + + if (st->rx_len < (sizeof(*r) + sizeof(r->desc[0]) * st->num_returned)) + return -EINVAL; + + /* Initialized to first descriptor and initial payload size */ + p->next =3D (void *)r->desc; + p->rx_len =3D st->rx_len - sizeof(*r); + + return 0; +} + +static inline size_t scmi_telemetry_tde_var_size_calc(struct telemetry_de = *tde) +{ + size_t sz =3D 0; + + sz +=3D tde->ts_type =3D=3D TSTAMP_LINE ? sizeof(__le32) : 0; + sz +=3D tde->de.fc_support ? sizeof(struct de_desc_fc) : 0; + sz +=3D tde->de.name_support ? SCMI_SHORT_NAME_MAX_SIZE : 0; + + return sz; +} + +static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti, + struct telemetry_de *tde, + void **next, size_t *rx_len) +{ + const struct scmi_de_desc *desc =3D *next; + unsigned int grp_id; + size_t payld_sz =3D sizeof(*desc); + + if (*rx_len < payld_sz) + return -ENOSPC; + + tde->de.info->id =3D le32_to_cpu(desc->id); + grp_id =3D le32_to_cpu(desc->grp_id); + if (grp_id !=3D SCMI_TLM_GRP_INVALID) { + struct scmi_telemetry_res_info *rinfo; + + /* Get rinfo without triggering a recursive enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + /* Group descriptors are empty but allocated at this point */ + if (grp_id >=3D ti->info.base.num_groups) + return -EINVAL; + + /* Link to parent group */ + tde->de.grp =3D &rinfo->grps[grp_id]; + } + + tde->de.info->grp_id =3D grp_id; + tde->de.info->data_sz =3D le32_to_cpu(desc->data_sz); + tde->de.info->type =3D GET_DE_TYPE(desc); + tde->de.info->unit =3D GET_DE_UNIT(desc); + tde->de.info->unit_exp =3D GET_DE_UNIT_EXP(desc); + tde->de.info->instance_id =3D GET_DE_INSTA_ID(desc); + tde->de.info->compo_instance_id =3D GET_COMPO_INSTA_ID(desc); + tde->de.info->compo_type =3D GET_COMPO_TYPE(desc); + tde->de.info->persistent =3D IS_PERSISTENT(desc); + tde->ts_type =3D TSTAMP_SUPPORT(desc); + tde->de.tstamp_support =3D !!tde->ts_type; + tde->de.fc_support =3D IS_FC_SUPPORTED(desc); + tde->de.name_support =3D IS_NAME_SUPPORTED(desc); + /* Update DE_DESCRIPTOR size for the next iteration */ + *next +=3D sizeof(*desc); + + payld_sz +=3D scmi_telemetry_tde_var_size_calc(tde); + if (*rx_len < payld_sz) + return -ENOSPC; + + if (tde->ts_type =3D=3D TSTAMP_LINE) { + __le32 *line_ts_rate =3D *next; + + tde->de.info->ts_rate =3D le32_to_cpu((*line_ts_rate)); + + /* Variably sized depending on TS support */ + *next +=3D sizeof(*line_ts_rate); + } else if (tde->ts_type =3D=3D TSTAMP_BLK) { + /* Setup default BLK TS value at first */ + tde->de.info->ts_rate =3D ti->default_blk_ts_rate; + } + + if (tde->de.fc_support) { + u32 size; + u64 phys_addr; + void __iomem *addr; + struct de_desc_fc *dfc; + + dfc =3D *next; + phys_addr =3D le32_to_cpu(dfc->addr_low); + phys_addr |=3D (u64)le32_to_cpu(dfc->addr_high) << 32; + + size =3D le32_to_cpu(dfc->size); + addr =3D devm_ioremap(ti->ph->dev, phys_addr, size); + if (!addr) + return -EADDRNOTAVAIL; + + tde->base =3D addr; + tde->offset =3D 0; + tde->fc_size =3D size; + + /* Variably sized depending on FC support */ + *next +=3D sizeof(*dfc); + } + + if (tde->de.name_support) { + const char *de_name =3D *next; + + strscpy(tde->de.info->name, de_name, SCMI_SHORT_NAME_MAX_SIZE); + /* Variably sized depending on name support */ + *next +=3D SCMI_SHORT_NAME_MAX_SIZE; + } + + /* Count timestamped DEs */ + ti->num_des_tstamp +=3D !!tde->de.tstamp_support; + + *rx_len -=3D payld_sz; + + return 0; +} + +static int iter_de_descr_process_response(const struct scmi_protocol_handl= e *ph, + const void *response, + struct scmi_iterator_state *st, + void *priv) +{ + struct scmi_tlm_de_priv *p =3D priv; + struct telemetry_info *ti =3D p->ti; + const struct scmi_de_desc *desc =3D p->next; + struct telemetry_de *tde; + bool discovered =3D false; + unsigned int de_id; + int ret; + + de_id =3D le32_to_cpu(desc->id); + /* Check if this DE has already been discovered by other means... */ + tde =3D scmi_telemetry_tde_lookup(ti, de_id); + if (!tde) { + /* Create a new one */ + tde =3D scmi_telemetry_tde_get(ti, de_id); + if (IS_ERR(tde)) + return PTR_ERR(tde); + + discovered =3D true; + } else if (tde->enumerated) { + /* Cannot be a duplicate of a DE already created by enumeration */ + dev_err(ph->dev, + "Discovered INVALID DE with DUPLICATED ID:0x%08X\n", + de_id); + return -EINVAL; + } + + ret =3D scmi_telemetry_de_descriptor_parse(ti, tde, &p->next, &p->rx_len); + if (ret) { + dev_err(ph->dev, "Malformed DE Descriptor - ret:%d\n", ret); + goto err; + } + + /* Now fully enumerated */ + tde->enumerated =3D true; + + if (discovered) { + /* Register if it was not already ... */ + ret =3D scmi_telemetry_tde_register(ti, tde); + if (ret) + goto err; + } + + /* Add to FastChannels list */ + if (tde->de.fc_support) + list_add(&tde->item, &ti->fcs_des); + + /* Account for this DE in group num_de counter */ + if (tde->de.grp) + tde->de.grp->info->num_des++; + + return 0; + +err: + /* DE not enumerated at this point were created in this call */ + if (discovered) + scmi_telemetry_free_tde_put(ti, tde); + + return ret; +} + +static int +scmi_telemetry_de_groups_init(struct device *dev, struct telemetry_info *t= i) +{ + struct scmi_telemetry_res_info *rinfo; + unsigned int num_groups =3D 0; + + /* Get rinfo without triggering a recursive enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + /* Allocate all groups DEs IDs arrays at first ... */ + for (int i =3D 0; i < ti->info.base.num_groups; i++) { + struct scmi_telemetry_group *grp =3D &rinfo->grps[i]; + size_t des_str_sz; + + unsigned int *des __free(kfree) =3D kcalloc(grp->info->num_des, + sizeof(unsigned int), + GFP_KERNEL); + if (!des) + break; + + /* + * Max size 32bit ID string in Hex: 0xCAFECAFE + * - 10 digits + ' '/'\n' =3D 11 bytes per number + * - terminating NUL character + */ + des_str_sz =3D grp->info->num_des * 11 + 1; + char *des_str __free(kfree) =3D kzalloc(des_str_sz, GFP_KERNEL); + if (!des_str) + break; + + grp->des =3D no_free_ptr(des); + grp->des_str =3D no_free_ptr(des_str); + /* Reset group DE counter */ + grp->info->num_des =3D 0; + + num_groups++; + } + + /* Unroll on failure... */ + if (num_groups < ti->info.base.num_groups) { + for (int i =3D 0; i < num_groups; i++) { + kfree(rinfo->grps[i].des); + rinfo->grps[i].des =3D NULL; + kfree(rinfo->grps[i].des_str); + rinfo->grps[i].des_str =3D NULL; + } + + return -ENOMEM; + } + + /* Scan DEs and populate DE IDs arrays for all groups */ + for (int i =3D 0; i < rinfo->num_des; i++) { + struct scmi_telemetry_group *grp =3D rinfo->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++] =3D i; + } + + /* Build composing DES string */ + for (int i =3D 0; i < ti->info.base.num_groups; i++) { + struct scmi_telemetry_group *grp =3D &rinfo->grps[i]; + size_t bufsize =3D grp->info->num_des * 11 + 1; + char *buf =3D grp->des_str; + + for (int j =3D 0; j < grp->info->num_des; j++) { + char term =3D j !=3D (grp->info->num_des - 1) ? ' ' : '\0'; + int len; + + len =3D scnprintf(buf, bufsize, "0x%08X%c", + rinfo->des[grp->des[j]]->info->id, term); + + buf +=3D len; + bufsize -=3D len; + } + } + + /* Expose all groups once all fully initialized */ + rinfo->num_groups =3D num_groups; + + return 0; +} + +static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti) +{ + const struct scmi_protocol_handle *ph =3D ti->ph; + + struct scmi_iterator_ops ops =3D { + .prepare_message =3D iter_tlm_prepare_message, + .update_state =3D iter_de_descr_update_state, + .process_response =3D iter_de_descr_process_response, + }; + struct scmi_tlm_de_priv tpriv =3D { + .ti =3D ti, + .next =3D NULL, + }; + void *iter; + int ret; + + if (!ti->info.base.num_des) + return 0; + + iter =3D 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 =3D ph->hops->iter_response_run(iter); + if (ret) + return ret; + + return scmi_telemetry_de_groups_init(ph->dev, ti); +} + +struct scmi_tlm_ivl_priv { + struct device *dev; + struct scmi_telemetry_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 =3D message; + const struct scmi_tlm_ivl_priv *p =3D priv; + + msg->index =3D cpu_to_le32(desc_index); + msg->group_identifier =3D cpu_to_le32(p->grp_id); + msg->flags =3D cpu_to_le32(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 =3D response; + + st->num_returned =3D le32_get_bits(r->flags, GENMASK(11, 0)); + st->num_remaining =3D le32_get_bits(r->flags, GENMASK(31, 16)); + + if (st->rx_len < (sizeof(*r) + sizeof(r->intervals[0]) * st->num_returned= )) + return -EINVAL; + + /* + * 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 =3D priv; + struct scmi_telemetry_intervals *intrvs; + bool discrete; + int inum; + + discrete =3D INTERVALS_DISCRETE(r->flags); + /* Check consistency on first call */ + if (!discrete && (st->num_returned !=3D 3 || st->num_remaining !=3D 0)) + return -EINVAL; + + inum =3D st->num_returned + st->num_remaining; + intrvs =3D kzalloc(sizeof(*intrvs) + inum * sizeof(__u32), GFP_KERNEL); + if (!intrvs) + return -ENOMEM; + + intrvs->num_intervals =3D inum; + intrvs->discrete =3D discrete; + st->max_resources =3D intrvs->num_intervals; + + *p->intrvs =3D 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 =3D response; + struct scmi_tlm_ivl_priv *p =3D priv; + struct scmi_telemetry_intervals *intrvs =3D *p->intrvs; + unsigned int idx =3D st->loop_idx; + + intrvs->update_intervals[st->desc_index + idx] =3D + le32_to_cpu(r->intervals[idx]); + + return 0; +} + +static int +scmi_tlm_enumerate_update_intervals(struct telemetry_info *ti, + struct scmi_telemetry_intervals **intervals, + int grp_id, unsigned int flags) +{ + struct scmi_iterator_ops ops =3D { + .prepare_message =3D iter_intervals_prepare_message, + .update_state =3D iter_intervals_update_state, + .process_response =3D iter_intervals_process_response, + }; + const struct scmi_protocol_handle *ph =3D ti->ph; + struct scmi_telemetry_intervals *intrvs =3D NULL; + struct scmi_tlm_ivl_priv ipriv =3D { + .dev =3D ph->dev, + .grp_id =3D grp_id, + .intrvs =3D &intrvs, + .flags =3D flags, + }; + void *iter; + int ret; + + iter =3D 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); + + /* Cleanup iterator inner allocations on failure */ + ret =3D ph->hops->iter_response_run(iter); + if (ret) { + kfree(intrvs); + return ret; + } + + *intervals =3D intrvs; + + return 0; +} + +static int +scmi_telemetry_enumerate_groups_intervals(struct telemetry_info *ti) +{ + struct scmi_telemetry_res_info *rinfo; + + if (!ti->info.per_group_config_support) + return 0; + + /* Get rinfo without triggering a recursive enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + for (int id =3D 0; id < rinfo->num_groups; id++) { + int ret; + + ret =3D scmi_tlm_enumerate_update_intervals(ti, + &rinfo->grps[id].intervals, + id, SPECIFIC_GROUP_DES); + if (ret) + return ret; + + rinfo->grps_store[id].num_intervals =3D + rinfo->grps[id].intervals->num_intervals; + } + + return 0; +} + +static void scmi_telemetry_intervals_free(void *interval) +{ + kfree(interval); +} + +static int +scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) +{ + unsigned int flags; + int ret; + + flags =3D !ti->info.per_group_config_support ? + ALL_DES_ANY_GROUP : ALL_DES_NO_GROUP; + + ret =3D scmi_tlm_enumerate_update_intervals(ti, &ti->info.intervals, + SCMI_TLM_GRP_INVALID, flags); + if (ret) + return ret; + + /* A copy for UAPI access... */ + ti->info.base.num_intervals =3D ti->info.intervals->num_intervals; + + /* Delegate freeing of allocated intervals to unbind time */ + return devm_add_action_or_reset(ti->ph->dev, + scmi_telemetry_intervals_free, + ti->info.intervals); +} + +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 =3D response; + + st->num_returned =3D le32_get_bits(r->num_shmti, GENMASK(15, 0)); + st->num_remaining =3D le32_get_bits(r->num_shmti, GENMASK(31, 16)); + + if (st->rx_len < (sizeof(*r) + sizeof(r->desc[0]) * st->num_returned)) + return -EINVAL; + + return 0; +} + +static inline int +scmi_telemetry_shmti_validate(struct device *dev, struct telemetry_shmti *= shmti) +{ + struct tdcf __iomem *tdcf =3D shmti->base; + u32 sign_start, sign_end; + + sign_start =3D TDCF_START_SIGNATURE(tdcf); + sign_end =3D TDCF_END_SIGNATURE(SHMTI_EPLG(shmti)); + + if (sign_start !=3D SIGNATURE_START || sign_end !=3D SIGNATURE_END) { + dev_err(dev, + "BAD signature for SHMTI ID:%u @phys:%pK - START:0x%04X END:0x%04X\n", + shmti->info.sid, shmti->base, sign_start, sign_end); + return -EINVAL; + } + + 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 =3D response; + struct telemetry_info *ti =3D priv; + struct telemetry_shmti *shmti; + const struct scmi_shmti_desc *desc; + void __iomem *addr; + u64 phys_addr; + u32 len; + + desc =3D &r->desc[st->loop_idx]; + shmti =3D &ti->shmti[st->desc_index + st->loop_idx]; + + shmti->info.sid =3D le32_to_cpu(desc->id); + shmti->flags =3D le32_to_cpu(desc->flags); + phys_addr =3D le32_to_cpu(desc->addr_low); + phys_addr |=3D (u64)le32_to_cpu(desc->addr_high) << 32; + + if (!IS_ALIGNED(phys_addr, 4)) { + dev_err(ph->dev, + "SHMTI ID %u is NOT 32-bit aligned (paddr:0x%llX). Abort.\n", + shmti->info.sid, phys_addr); + return -EINVAL; + } + + len =3D le32_to_cpu(desc->length); + if (len < SHMTI_MIN_SIZE) { + dev_err(ph->dev, "Invalid length for SHMTI ID:%u len:%u\n", + shmti->info.sid, len); + return -EINVAL; + } + + /* SHMTI base phys_addr SHOULD be 64bit aligned by the SCMI spec */ + if (!IS_ALIGNED(phys_addr, 8)) + dev_warn(ph->dev, + "SHMTI ID:%u is NOT 64bit aligned (paddr:0x%llX)\n", + shmti->info.sid, phys_addr); + + addr =3D devm_ioremap(ph->dev, phys_addr, len); + if (!addr) + return -EADDRNOTAVAIL; + + shmti->base =3D addr; + shmti->info.len =3D len; + shmti->info.phys =3D phys_addr; + shmti->info.offset =3D shmti->info.phys & ~PAGE_MASK; + + return scmi_telemetry_shmti_validate(ph->dev, shmti); +} + +static int scmi_telemetry_shmti_list(const struct scmi_protocol_handle *ph, + struct telemetry_info *ti) +{ + struct scmi_iterator_ops ops =3D { + .prepare_message =3D iter_tlm_prepare_message, + .update_state =3D iter_shmti_update_state, + .process_response =3D iter_shmti_process_response, + }; + void *iter; + + iter =3D ph->hops->iter_response_init(ph, &ops, ti->num_shmti, + 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(struct telemetry_info *ti) +{ + const struct scmi_protocol_handle *ph =3D ti->ph; + struct scmi_telemetry_shmti_info **shmtis; + int ret; + + if (!ti->num_shmti) + return 0; + + ti->shmti =3D devm_kcalloc(ph->dev, ti->num_shmti, sizeof(*ti->shmti), + GFP_KERNEL); + if (!ti->shmti) + return -ENOMEM; + + ret =3D scmi_telemetry_shmti_list(ph, ti); + if (ret) { + dev_err(ph->dev, "Cannot get SHMTI list descriptors"); + return ret; + } + + /* Expose initialized SHMTIs references */ + shmtis =3D devm_kcalloc(ph->dev, ti->num_shmti, + sizeof(*shmtis), GFP_KERNEL); + if (!shmtis) + return -ENOMEM; + + for (int i =3D 0; i < ti->num_shmti; i++) + shmtis[i] =3D &ti->shmti[i].info; + + ti->info.shmtis =3D shmtis; + ti->info.base.num_shmtis =3D ti->num_shmti; + + return 0; +} + +static const struct scmi_telemetry_info * +scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + + return &ti->info; +} + +static const struct scmi_telemetry_de * +scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + + ti->res_get(ti); + return xa_load(&ti->xa_des, id); +} + +static const struct scmi_telemetry_res_info * +scmi_telemetry_resources_get(const struct scmi_protocol_handle *ph) +{ + struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); + struct telemetry_info *ti =3D ph->get_priv(ph); + + /* Ensure the latest installed res_get helper is visible */ + res_get =3D smp_load_acquire(&ti->res_get); + + return res_get(ti); +} + +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, +}; + +/** + * scmi_telemetry_resources_alloc - Resources allocation + * @ti: A reference to the telemetry info descriptor for this instance + * + * This allocates and initializes dedicated resources for the maximum poss= ible + * number of needed telemetry resources, based on information gathered from + * the initial enumeration: these allocations represent an upper bound on + * the number of discoverable telemetry resources and they will be later + * populated during late deferred further discovery phases. + * + * Return: 0 on Success, errno otherwise + */ +static int scmi_telemetry_resources_alloc(struct telemetry_info *ti) +{ + /* Array to hold pointers to discovered DEs */ + struct scmi_telemetry_de **des __free(kfree) =3D + kcalloc(ti->info.base.num_des, sizeof(*des), GFP_KERNEL); + if (!des) + return -ENOMEM; + + /* The allocated DE descriptors */ + struct telemetry_de *tdes __free(kfree) =3D + kcalloc(ti->info.base.num_des, sizeof(*tdes), GFP_KERNEL); + if (!tdes) + return -ENOMEM; + + /* Allocate a set of contiguous DE info descriptors. */ + struct scmi_telemetry_de_info *dei_store __free(kfree) =3D + kcalloc(ti->info.base.num_des, sizeof(*dei_store), GFP_KERNEL); + if (!dei_store) + return -ENOMEM; + + /* Array to hold descriptors of discovered GROUPs */ + struct scmi_telemetry_group *grps __free(kfree) =3D + kcalloc(ti->info.base.num_groups, sizeof(*grps), GFP_KERNEL); + if (!grps) + return -ENOMEM; + + /* Allocate a set of contiguous Group info descriptors. */ + struct scmi_telemetry_grp_info *grps_store __free(kfree) =3D + kcalloc(ti->info.base.num_groups, sizeof(*grps_store), GFP_KERNEL); + if (!grps_store) + return -ENOMEM; + + struct scmi_telemetry_res_info *rinfo __free(kfree) =3D + kzalloc(sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + mutex_init(&ti->free_mtx); + INIT_LIST_HEAD(&ti->free_des); + for (int i =3D 0; i < ti->info.base.num_des; i++) { + mutex_init(&tdes[i].mtx); + /* Bind contiguous DE info structures */ + tdes[i].de.info =3D &dei_store[i]; + scmi_telemetry_free_tde_put(ti, &tdes[i]); + } + + for (int i =3D 0; i < ti->info.base.num_groups; i++) { + grps_store[i].grp_id =3D i; + /* Bind contiguous Group info struct */ + grps[i].info =3D &grps_store[i]; + } + + INIT_LIST_HEAD(&ti->fcs_des); + + ti->tdes =3D no_free_ptr(tdes); + + rinfo->des =3D no_free_ptr(des); + rinfo->dei_store =3D no_free_ptr(dei_store); + rinfo->grps =3D no_free_ptr(grps); + rinfo->grps_store =3D no_free_ptr(grps_store); + + /* Ensure all of the above assignments are visible */ + smp_store_release(&ti->rinfo, no_free_ptr(rinfo)); + + return 0; +} + +static void scmi_telemetry_groups_free(struct scmi_telemetry_res_info *rin= fo) +{ + for (int i =3D 0; i < rinfo->num_groups; i++) { + struct scmi_telemetry_group *grp =3D &rinfo->grps[i]; + + kfree(grp->des); + kfree(grp->des_str); + kfree(grp->intervals); + } +} + +static struct scmi_telemetry_res_info * +__scmi_telemetry_resources_get(struct telemetry_info *ti) +{ + /* Ensure rinfo descriptor is visible */ + return smp_load_acquire(&ti->rinfo); +} + +static void scmi_telemetry_resources_free(void *arg) +{ + struct scmi_telemetry_res_info *rinfo; + struct telemetry_info *ti =3D arg; + struct scmi_telemetry_de *de; + unsigned long idx; + + /* Get rinfo without triggering a recursive enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + /* Ensure rinfo is no more accessible upfront */ + smp_store_release(&ti->rinfo, NULL); + + xa_for_each(&ti->xa_des, idx, de) { + struct telemetry_de *tde =3D to_tde(de); + + scmi_telemetry_free_tde_put(ti, tde); + } + + xa_destroy(&ti->xa_des); + kfree(ti->tdes); + kfree(rinfo->des); + kfree(rinfo->dei_store); + scmi_telemetry_groups_free(rinfo); + kfree(rinfo->grps); + kfree(rinfo->grps_store); + + kfree(rinfo); + + dev_dbg(ti->ph->dev, "SCMI Telemetry resources freed for instance\n"); +} + +/** + * scmi_telemetry_resources_enumerate - Enumeration helper + * @ti: A reference to the telemetry info descriptor for this instance + * + * This helper is configured to be called once on the first enumeration + * attempt, when triggered by invoking ti->res_get() from somewhere else. + * + * Once run it substitues itself in ti->res_get() with the simple accessor + * __scmi_telemetry_resources_get, which returns a descriptor to the resou= rces + * that were possibly discovered. + * + * Note that, while it attempts to fully enumerate Data Events and Groups,= it + * does NOT fail when such enumerations fail, instead it simply gives up w= ith + * the end result that only a partially populated, but consistent, resourc= es + * descriptor will be returned; in such a case the incomplete descriptor w= ill + * be marked as NOT fully_enumerated: this design enables the kernel to de= al + * with badly implemented out-of-spec firmware support while keep on provi= ding + * a minimal sane, albeit possibly incomplete, set of telemetry respources. + * + * Return: A reference to a fully or partially populated resources descrip= tor + */ +static struct scmi_telemetry_res_info * +scmi_telemetry_resources_enumerate(struct telemetry_info *ti) +{ + struct device *dev =3D ti->ph->dev; + int ret; + + /* + * Ensure the following initialization can be called only once + * from one thread of execution. + */ + if (atomic_cmpxchg(&ti->rinfo_initializing, 0, 1)) { + /* + * When initialization is already ongoing in another thread, + * just wait for its completion and return the fully or partially + * populated rinfo. + */ + if (!completion_done(&ti->rinfo_initdone)) + wait_for_completion(&ti->rinfo_initdone); + + /* Ensure rinfo descriptor is visible */ + return smp_load_acquire(&ti->rinfo); + } + + /* Note that this code below can be run only once by one thread */ + ret =3D scmi_telemetry_de_descriptors_get(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot fully enumerate DEs resources. Degraded syst= em.\n"); + goto done; + } + + ret =3D scmi_telemetry_enumerate_groups_intervals(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot fully enumerate group intervals. Degraded sy= stem.\n"); + goto done; + } + + ti->rinfo->fully_enumerated =3D true; +done: + /* Disable initialization permanently */ + smp_store_release(&ti->res_get, __scmi_telemetry_resources_get); + + /* Unblock concurrent threads that have been stalled */ + complete_all(&ti->rinfo_initdone); + + /* Ensure local rinfo is visible before returning it */ + smp_mb(); + return READ_ONCE(ti->rinfo); +} + +/** + * scmi_telemetry_instance_init - Instance initializer + * @ti: A reference to the telemetry info descriptor for this instance + * + * Note that this allocates and initialize all the resources possibly need= ed + * and then setups the @scmi_telemetry_resources_enumerate helper as the + * default method for the first call to ti->res_get(): this mechanism enab= les + * the possibility of optionally implementing deferred enumeration policies + * which optionally delay the discovery phase and related SCMI message exc= hanges + * to a later point in time. + * + * Return: 0 on Success, errno otherwise + */ +static int scmi_telemetry_instance_init(struct telemetry_info *ti) +{ + int ret; + + /* Allocate and Initialize on first call... */ + ret =3D scmi_telemetry_resources_alloc(ti); + if (ret) + return ret; + + xa_init(&ti->xa_des); + ret =3D devm_add_action_or_reset(ti->ph->dev, + scmi_telemetry_resources_free, ti); + if (ret) + return ret; + + /* Setup resources lazy initialization */ + atomic_set(&ti->rinfo_initializing, 0); + init_completion(&ti->rinfo_initdone); + /* Ensure the new res_get() operation is visible after this point */ + smp_store_mb(ti->res_get, scmi_telemetry_resources_enumerate); + + return 0; +} + +static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle = *ph) +{ + struct device *dev =3D ph->dev; + struct telemetry_info *ti; + int ret; + + dev_dbg(dev, "Telemetry Version %d.%d\n", + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); + + ti =3D devm_kzalloc(dev, sizeof(*ti), GFP_KERNEL); + if (!ti) + return -ENOMEM; + + ti->ph =3D ph; + + ret =3D scmi_telemetry_protocol_attributes_get(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot retrieve protocol attributes. Abort.\n"); + return ret; + } + + ret =3D scmi_telemetry_instance_init(ti); + if (ret) { + dev_err(dev, "Cannot initialize instance. Abort.\n"); + return ret; + } + + ret =3D scmi_telemetry_enumerate_common_intervals(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot enumerate update intervals. Abort.\n"); + return ret; + } + + ret =3D scmi_telemetry_enumerate_shmti(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot enumerate SHMTIs. Abort.\n"); + return ret; + } + + ti->info.base.version =3D ph->version; + + return ph->set_priv(ph, ti); +} + +static const struct scmi_protocol scmi_telemetry =3D { + .id =3D SCMI_PROTOCOL_TELEMETRY, + .owner =3D THIS_MODULE, + .instance_init =3D &scmi_telemetry_protocol_init, + .ops =3D &tlm_proto_ops, + .supported_version =3D 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 5ab73b1ab9aa..2850b018da0d 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -2,16 +2,18 @@ /* * SCMI Message Protocol driver header * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2026 ARM Ltd. */ =20 #ifndef _LINUX_SCMI_PROTOCOL_H #define _LINUX_SCMI_PROTOCOL_H =20 #include +#include #include #include #include +#include =20 #define SCMI_MAX_STR_SIZE 64 #define SCMI_SHORT_NAME_MAX_SIZE 16 @@ -824,6 +826,184 @@ struct scmi_pinctrl_proto_ops { int (*pin_free)(const struct scmi_protocol_handle *ph, u32 pin); }; =20 +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 =3D 0xef, + SCMI_TLM_DE_TYPE_OEM_START =3D 0xf0, + SCMI_TLM_DE_TYPE_OEM_END =3D 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 =3D 0x1d, + SCMI_TLM_COMPO_TYPE_RESERVED_END =3D 0xdf, + SCMI_TLM_COMPO_TYPE_OEM_START =3D 0xe0, + SCMI_TLM_COMPO_TYPE_OEM_END =3D 0xff, +}; + +#define SCMI_TLM_GET_UPDATE_INTERVAL_SECS(x) (FIELD_GET(GENMASK(20, 5), (x= ))) +#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP(x) (sign_extend32((x), 4)) + +#define SCMI_TLM_GET_UPDATE_INTERVAL(x) (FIELD_GET(GENMASK(20, 0), (x))) +#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_intervals { + unsigned int grp_id; + bool discrete; + unsigned int num_intervals; +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_LOW 0 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_HIGH 1 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_STEP 2 + unsigned int update_intervals[] __counted_by(num_intervals); +}; + +struct scmi_telemetry_grp_info { + unsigned int grp_id; + unsigned int num_des; + unsigned int num_intervals; +}; + +struct scmi_telemetry_group { + bool enabled; + bool tstamp_enabled; + unsigned int *des; + char *des_str; + struct scmi_telemetry_grp_info *info; + unsigned int active_update_interval; + struct scmi_telemetry_intervals *intervals; + enum scmi_telemetry_collection current_mode; +}; + +struct scmi_telemetry_de_info { + unsigned int id; + unsigned int grp_id; + unsigned int data_sz; + unsigned int type; + unsigned int unit; + int unit_exp; + unsigned int ts_rate; + unsigned int instance_id; + unsigned int compo_instance_id; + unsigned int compo_type; + bool persistent; + char name[16]; +}; + +struct scmi_telemetry_de { + bool tstamp_support; + bool fc_support; + bool name_support; + struct scmi_telemetry_de_info *info; + struct scmi_telemetry_group *grp; + bool enabled; + bool tstamp_enabled; +}; + +struct scmi_telemetry_res_info { + bool fully_enumerated; + unsigned int num_des; + struct scmi_telemetry_de **des; + struct scmi_telemetry_de_info *dei_store; + unsigned int num_groups; + struct scmi_telemetry_group *grps; + struct scmi_telemetry_grp_info *grps_store; +}; + +struct scmi_telemetry_base_info { + unsigned int version; + uuid_t primary_revision; + unsigned int num_des; + unsigned int num_groups; + unsigned int num_intervals; + unsigned int num_shmtis; +}; + +struct scmi_telemetry_shmti_info { + unsigned int sid; + unsigned int len; + unsigned long offset; + phys_addr_t phys; +}; + +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_telemetry_base_info base; + unsigned int active_update_interval; + struct scmi_telemetry_intervals *intervals; + struct scmi_telemetry_shmti_info **shmtis; + unsigned int num_uuids; + uuid_t **uuids; + bool enabled; + bool notif_enabled; + enum scmi_telemetry_collection current_mode; +}; + +/** + * struct scmi_telemetry_proto_ops - represents the various operations pro= vided + * by SCMI Telemetry Protocol + * + * @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. + */ +struct scmi_telemetry_proto_ops { + const struct scmi_telemetry_info __must_check *(*info_get) + (const struct scmi_protocol_handle *ph); + const struct scmi_telemetry_de __must_check *(*de_lookup) + (const struct scmi_protocol_handle *ph, u32 id); + const struct scmi_telemetry_res_info __must_check *(*res_get) + (const struct scmi_protocol_handle *ph); +}; + /** * struct scmi_notify_ops - represents notifications' operations provided= by * SCMI core @@ -930,6 +1110,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_VOLTAGE =3D 0x17, SCMI_PROTOCOL_POWERCAP =3D 0x18, SCMI_PROTOCOL_PINCTRL =3D 0x19, + SCMI_PROTOCOL_TELEMETRY =3D 0x1b, }; =20 enum scmi_system_events { --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 337553ED5C7; Sun, 20 Sep 2026 09:20:42 +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=1789896047; cv=none; b=JYXfCBHb32h1XXnY+VeCTZGntD5twmsABWH9THwm4sYLcNgtpYkjxkefzyro1VKUj5CddmpGZmNAmzAOIFJ2YRyCz6shuItGSKlee6tECbKTRDR/1eRWV88og5I6F8tP1WI2sOdA9WpuYpfu0TEbMLY7lidtppjTvK62pHYdJHE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896047; c=relaxed/simple; bh=8sJ0aasFFtzfzQEvhMHO5G4SphRq0PAvm6rvO5lTJqg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nL7ugeCw3fGFvhhNEp47qT7IeWE8bilsGkEb1nFD/WxMfiDMjfc/72cLG+lka4ZTtSHtn811H84CO72E7lqVkxgZRkIMcBgicoaoI3NBEEYlCLv6VZs3dvhkZYNuI1i7igvP9oOBP6moJSSB3/0jas9i0lGdaTXAWBzuVNdCPH8= 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=VZlvkKLW; 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="VZlvkKLW" 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 9CAAC1CE0; Sun, 20 Sep 2026 02:20:36 -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 6778E3F59E; Sun, 20 Sep 2026 02:20:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896040; bh=8sJ0aasFFtzfzQEvhMHO5G4SphRq0PAvm6rvO5lTJqg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VZlvkKLW0XBjspMJfHEeilmuUtoef7/P0gOrsDMm2b+NKX7Q+WCczJXehUSi5m6/D dlw6w6W5f5R4/lx9XFaQX3JKChviablgorCJirasWMrgEkIVMVLHJyK46C8LKkQ6Us Od60ZOHhU6v6fpj/qa+8dNv22U/sIg+MOnnO91/E= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 07/25] firmware: arm_scmi: Add support to parse SHMTIs areas Date: Sun, 20 Sep 2026 10:19:10 +0100 Message-ID: <20260920091928.2014972-8-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 logic to scan the SHMTI areas, parsing the TDCF descriptors while collecting DataEvent, BlockTimestamp and UUID lines. Signed-off-by: Cristian Marussi --- v11 --> v12 - reject zero as MATCHSEQ - reworked locking around line_get/put - reworked locking around blkts_read - a few comments on locking acquisition order - cleanup active line refs even when bailing out of a scan in error - added hashed locks for fine grain locking around blkts/uuid line creation - added RCU protection on UUIDs access v10 --> v11 - relocated some line handlers from another patch - reworked TDE and lines locking logic - fix UUID/BLKTS line refcounting to properly drop unused lines - add a mutex to protect des cache accesses v7 --> v9 - avoid referencing a NULL in trace_scmi_tlm_access O_o - refactor scmi_telemetry_line_put to free its container on release - convert SHMTI_OVERFLOW macro to an inline function v7 --> v8 - Fixes for some Sashiko issues on v7 - reword a few dev_errs - removed a few unused function (moved to next patch) - add more safeguards and checks to SHMTI_OVERFLOWS - fix UUIDs copy and allocation - fix UUID reference store and cleanup - cleanup all UNBOUND lines and their containers in resources_free - remove mutex on primary init v6 --> v7 - add _RAW helpers to manage non MMIO accesses - sparse fixes - use uuid_t for UUID lines - fix proper UUID line parsing and binding - adding a few traces for TDCF line handling - add new dynamically sized UUID DB to ease discovered UUIDs lookup - fix endianity handling in UUID TDCF lines - handle Primary UUID lines with regular uuid_line v5 --> v6 - consider UUID line in TDCF as BE when scanning - use const booleans in scmi_telemetry_de_state_update() - fix Sashiko review https://sashiko.dev/#/patchset/20260703123601.381275-= 1-cristian.marussi%40arm.com?part=3D8 - check SHMTI overflow while parsing - use proper bytes increment while parsing line_by_type - use mutex guard when link BLK_TS lines v4 --> v5 - converted TDE_HAS_TSTAMP to inline scmi_tde_has_tstamp - fixes sparse warnings v3 --> v4 - use kzalloc_obj - track SHMTI-discovered Telemetry DE states - refactor TDE cache with dedicated helpers - force tstamp to zero when timestamp is NOT supported or disabled v2 --> v3 - split from monolithic Telemetry patch - avoid devres allocation for resources that are added to the xa_lines XAr= ray - simplify prototype of line parsing helpers to drop unneeded dev - flip tstmap logic in scmi_telemetry_line_data_parse() to properly emit a TLM ftrace event - use ternary ops to simplify quite a few expressions --- drivers/firmware/arm_scmi/telemetry.c | 934 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 2 +- 2 files changed, 932 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index d8176a7d78b8..c5d17a4d9bb3 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -12,10 +12,13 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -257,6 +260,41 @@ struct uuid_line { __be32 dwords[SCMI_TLM_DE_IMPL_NUM_DWORDS]; }; =20 +#define LINE_DATA_GET_RAW(f) \ +({ \ + typeof(f) _f =3D (f); \ + u32 _high =3D le32_to_cpu(_f->data_high); \ + u32 _low =3D le32_to_cpu(_f->data_low); \ + \ + (TO_CPU_64(_high, _low)); \ +}) + +#define LINE_DATA_GET(f) \ +({ \ + typeof(f) _f =3D (f); \ + \ + (TO_CPU_64(_I(&_f->data_high), _I(&_f->data_low))); \ +}) + +#define LINE_TSTAMP_GET_RAW(f) \ +({ \ + typeof(f) _f =3D (f); \ + u32 _high =3D le32_to_cpu(_f->ts_high); \ + u32 _low =3D le32_to_cpu(_f->ts_low); \ + \ + (TO_CPU_64(_high, _low)); \ +}) + +#define LINE_TSTAMP_GET(f) \ +({ \ + typeof(f) _f =3D (f); \ + \ + (TO_CPU_64(_I(&_f->ts_high), _I(&_f->ts_low))); \ +}) + +#define BLK_TS_STAMP(f) LINE_TSTAMP_GET(f) +#define BLK_TS_RATE(p) PAYLD_ID(p) + enum tdcf_line_types { TDCF_DATA_LINE =3D 0, TDCF_BLK_TS_LINE =3D 1, @@ -320,6 +358,7 @@ struct payload { #define LINE_LENGTH_WORDS(x) LINE_LENGTH_WORDS_RAW(PAYLD_META(x)) =20 #define LINE_LENGTH_QWORDS(x) ((LINE_LENGTH_WORDS(x)) / 2) +#define LINE_LENGTH_BYTES(x) ((LINE_LENGTH_WORDS(x)) * sizeof(u32)) =20 struct prlg { u32 sign_start; @@ -349,7 +388,9 @@ struct tdcf { =20 #define TDCF_START_SIGNATURE(x) (_I(&((x)->prlg.sign_start))) #define TDCF_START_SEQ_GET(x) (_I(&((x)->prlg.match_start))) -#define IS_BAD_START_SEQ(s) ((s) & 0x1) +/* TDCF MatchSequence cannot be zero by the SCMI v4.0 Specification */ +#define TDCF_INVALID_MATCH_SEQ 0 +#define IS_BAD_START_SEQ(s) ((s) & 0x1 || (s) =3D=3D TDCF_INVALID_MATCH_SE= Q) =20 #define TDCF_END_SEQ_GET(e) (_I(&((e)->match_end))) #define TDCF_END_SIGNATURE(e) (_I(&((e)->sign_end))) @@ -396,7 +437,8 @@ struct telemetry_uuid { struct telemetry_line line; }; =20 -#define to_uuid(l) container_of(l, struct telemetry_uuid, line) +#define to_uuid_from_line(l) container_of(l, struct telemetry_uuid, line) +#define to_uuid_from_uuid_t(u) container_of(u, struct telemetry_uuid, uuid) =20 enum timestamps { TSTAMP_NONE =3D 0, @@ -428,18 +470,40 @@ struct telemetry_de { =20 #define to_tde(d) container_of(d, struct telemetry_de, de) =20 +static inline bool scmi_tde_has_tstamp(struct telemetry_de *t) +{ + return t->de.tstamp_support && t->de.tstamp_enabled; +} + #define DE_ENABLED_WITH_TSTAMP 2 =20 +enum de_state { + ENA_STATE, + ENA_TSTAMP, + ENA_MAX +}; + struct telemetry_info { bool streaming_mode; unsigned int num_shmti; unsigned int num_des_tstamp; +#define SCMI_UUID_DB_THRESH 3 + /* Protect uuids_len */ + struct mutex uuids_mtx; + unsigned int uuids_len; + /* Mutex to protect des_enabled_mtx */ + struct mutex des_enabled_mtx; + atomic_t des_enabled[ENA_MAX]; unsigned int default_blk_ts_rate; const struct scmi_protocol_handle *ph; struct telemetry_shmti *shmti; struct telemetry_de *tdes; struct scmi_telemetry_group *grps; struct xarray xa_des; + /* Mutex to protect @xa_lines */ + struct mutex lines_mtx; + struct xarray xa_lines; + struct telemetry_uuid *primary_uuid; /* Mutex to protect access to @free_des */ struct mutex free_mtx; struct list_head free_des; @@ -447,6 +511,8 @@ struct telemetry_info { struct scmi_telemetry_info info; atomic_t rinfo_initializing; struct completion rinfo_initdone; + #define PAYLD_HASH_BITS 4 + struct mutex payld_locks[1 << PAYLD_HASH_BITS]; struct scmi_telemetry_res_info *rinfo; struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); }; @@ -454,6 +520,34 @@ 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 inline void scmi_telemetry_uuid_link(struct telemetry_de *tde, + struct telemetry_uuid *uuid); + +static inline +struct mutex *lock_for_payld(struct mutex *locks, struct payload __iomem *= payld) +{ + return &locks[hash_long((__force unsigned long)payld, PAYLD_HASH_BITS)]; +} + +static inline void +scmi_telemetry_de_state_update(struct telemetry_info *ti, enum de_state st= ate, + bool *current_state, const bool next_state) +{ + guard(mutex)(&ti->des_enabled_mtx); + if (!current_state || *current_state !=3D next_state) + atomic_add(next_state ? 1 : -1, &ti->des_enabled[state]); + + if (current_state) + *current_state =3D next_state; + + dev_dbg(ti->ph->dev, "Telemetry des_enabled[%s]:%u\n", + state =3D=3D ENA_STATE ? "STATE" : "TSTAMP", + atomic_read(&ti->des_enabled[state])); +} + static struct telemetry_de * scmi_telemetry_free_tde_get(struct telemetry_info *ti) { @@ -546,6 +640,27 @@ static int scmi_telemetry_tde_register(struct telemetr= y_info *ti, return ret; } =20 +static bool +scmi_telemetry_tde_cache_unchanged(struct telemetry_de *tde, u32 magic) +{ + guard(mutex)(&tde->mtx); + + return tde->last_magic =3D=3D magic; +} + +static void +scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val, + u64 *tstamp, u32 *magic) +{ + guard(mutex)(&tde->mtx); + + tde->last_magic =3D magic ? *magic : TDCF_BAD_END_SEQ; + tde->last_val =3D val; + tde->last_ts =3D tstamp && scmi_tde_has_tstamp(tde) ? *tstamp : 0; + if (tstamp) + *tstamp =3D tde->last_ts; +} + struct scmi_tlm_de_priv { struct telemetry_info *ti; void *next; @@ -773,8 +888,10 @@ static int iter_de_descr_process_response(const struct= scmi_protocol_handle *ph, } =20 /* Add to FastChannels list */ - if (tde->de.fc_support) + if (tde->de.fc_support) { + scmi_telemetry_uuid_link(tde, ti->primary_uuid); list_add(&tde->item, &ti->fcs_des); + } =20 /* Account for this DE in group num_de counter */ if (tde->de.grp) @@ -1250,6 +1367,734 @@ scmi_telemetry_resources_get(const struct scmi_prot= ocol_handle *ph) return res_get(ti); } =20 +static u64 +scmi_telemetry_blkts_read(u32 magic, struct telemetry_de *tde) +{ + struct telemetry_block_ts *bts; + + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (WARN_ON(!tde->bts)) + return 0; + + bts =3D tde->bts; + guard(mutex)(&bts->line.mtx); + + if (bts->line.last_magic =3D=3D magic) + return bts->last_ts; + + /* Note that the bts->last_rate can change ONLY on creation */ + bts->last_ts =3D BLK_TS_STAMP(&bts->line.payld->blk_tsl); + bts->line.last_magic =3D magic; + + return bts->last_ts; +} + +static void scmi_telemetry_blkts_update(struct telemetry_info *ti, u32 mag= ic, + struct telemetry_block_ts *bts) +{ + guard(mutex)(&bts->line.mtx); + + if (bts->line.last_magic !=3D magic) { + bts->last_ts =3D BLK_TS_STAMP(&bts->line.payld->blk_tsl); + bts->last_rate =3D BLK_TS_RATE(bts->line.payld); + /* BLK_TS clock rate value can change ONLY here on creation */ + if (!bts->last_rate) + bts->last_rate =3D ti->default_blk_ts_rate; + bts->line.last_magic =3D magic; + } +} + +static inline void *scmi_telemetry_line_blob_get(struct telemetry_line *li= ne) +{ + void *blob; + + switch (line->type) { + case TDCF_BLK_TS_LINE: + blob =3D to_blkts(line); + break; + case TDCF_UUID_LINE: + blob =3D to_uuid_from_line(line); + break; + default: + blob =3D NULL; + break; + } + + return blob; +} + +static void scmi_telemetry_line_put(struct telemetry_line *line) +{ + /* Acquiring coarse grain first ... */ + mutex_lock(&line->ti->lines_mtx); + mutex_lock(&line->mtx); + if (refcount_dec_and_test(&line->users)) { + xa_erase(&line->ti->xa_lines, (unsigned long)line->payld); + + /* Unlock and free the containing structure: blkts or uuid */ + mutex_unlock(&line->mtx); + mutex_unlock(&line->ti->lines_mtx); + + trace_scmi_tlm_access(0, "DROP_LINE", line->type, line->last_magic); + kfree(scmi_telemetry_line_blob_get(line)); + + return; + } + mutex_unlock(&line->mtx); + mutex_unlock(&line->ti->lines_mtx); +} + +static void scmi_telemetry_blkts_unlink(struct telemetry_de *tde) +{ + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (!tde->bts) + return; + + scmi_telemetry_line_put(&tde->bts->line); + tde->bts =3D NULL; + + trace_scmi_tlm_access(tde->de.info->id, "BLKTS_UNLINK", 0, 0); +} + +static void scmi_telemetry_uuid_unlink(struct telemetry_de *tde) +{ + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (!tde->uuid) + return; + + scmi_telemetry_line_put(&tde->uuid->line); + tde->uuid =3D NULL; + + trace_scmi_tlm_access(tde->de.info->id, "UUID_UNLINK", 0, 0); +} + +static void scmi_telemetry_de_unlink(struct scmi_telemetry_de *de) +{ + struct telemetry_de *tde =3D to_tde(de); + + /* Unlink all related lines triggering their deallocation */ + scmi_telemetry_blkts_unlink(tde); + scmi_telemetry_uuid_unlink(tde); +} + +static struct telemetry_line * +scmi_telemetry_line_get(struct telemetry_info *ti, struct payload __iomem = *payld) +{ + struct telemetry_line *line; + + /* Acquiring coarse grain first ... */ + guard(mutex)(&ti->lines_mtx); + line =3D xa_load(&ti->xa_lines, (unsigned long)payld); + if (!line) + return NULL; + + guard(mutex)(&line->mtx); + refcount_inc(&line->users); + + return line; +} + +static int +scmi_telemetry_line_init(struct telemetry_info *ti, struct telemetry_line = *line, + struct payload __iomem *payld, enum tdcf_line_types type) +{ + line->type =3D type; + refcount_set(&line->users, 1); + line->payld =3D payld; + line->ti =3D ti; + mutex_init(&line->mtx); + + guard(mutex)(&ti->lines_mtx); + return xa_insert(&ti->xa_lines, (unsigned long)payld, line, GFP_KERNEL); +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_create(struct telemetry_info *ti, + struct payload __iomem *payld) +{ + struct telemetry_block_ts *bts; + int ret; + + bts =3D kzalloc_obj(*bts); + if (!bts) + return NULL; + + ret =3D scmi_telemetry_line_init(ti, &bts->line, payld, TDCF_BLK_TS_LINE); + if (ret) { + kfree(bts); + return NULL; + } + + trace_scmi_tlm_collect(0, (__force u64)payld, 0, "SHMTI_NEW_BLKTS"); + + return bts; +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_get_or_create(struct telemetry_info *ti, + struct payload __iomem *payld) +{ + struct telemetry_line *line; + + /* Avoid races on line creation for this payld */ + guard(mutex)(lock_for_payld(ti->payld_locks, payld)); + line =3D scmi_telemetry_line_get(ti, payld); + if (line) + return to_blkts(line); + + return scmi_telemetry_blkts_create(ti, payld); +} + +static int scmi_telemetry_uuids_update(struct telemetry_info *ti, + struct telemetry_uuid *uuid) +{ + uuid_t **cur_uuids; + + guard(mutex)(&ti->uuids_mtx); + + /* Resize array if needed ... */ + if (ti->info.num_uuids + SCMI_UUID_DB_THRESH >=3D ti->uuids_len) { + uuid_t **new_uuids, **old_uuids; + unsigned int new_len =3D ti->uuids_len * 2; + + new_uuids =3D kcalloc(new_len, sizeof(*new_uuids), GFP_KERNEL); + if (!new_uuids) + return -ENOMEM; + + old_uuids =3D rcu_dereference_protected(ti->info.uuids, + lockdep_is_held(&ti->uuids_mtx)); + /* Copy/move old allocated UUIDs */ + for (int i =3D 0; i < ti->info.num_uuids; i++) + new_uuids[i] =3D old_uuids[i]; + + /* Expose new UUID pointer */ + rcu_assign_pointer(ti->info.uuids, new_uuids); + ti->uuids_len =3D new_len; + + kfree_rcu_mightsleep(old_uuids); + } + + /* Bump refcount on this line ... cannot fail by construction */ + scmi_telemetry_line_get(ti, uuid->line.payld); + /* Store new UUID reference into the globally accessible UUIDS */ + cur_uuids =3D rcu_dereference_protected(ti->info.uuids, + lockdep_is_held(&ti->uuids_mtx)); + cur_uuids[ti->info.num_uuids] =3D &uuid->uuid; + rcu_assign_pointer(ti->info.uuids, cur_uuids); + /* Update UUIDs count */ + smp_store_release(&ti->info.num_uuids, ti->info.num_uuids + 1); + + return 0; +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_create(struct telemetry_info *ti, + struct payload __iomem *payld) +{ + __be32 dwords[SCMI_TLM_DE_IMPL_NUM_DWORDS]; + struct uuid_line __iomem *uuid_l; + struct telemetry_uuid *uuid; + + uuid =3D kzalloc_obj(*uuid); + if (!uuid) + return NULL; + + /* A NULL payload is used for the unique primary UUID */ + if (!payld) + return uuid; + + uuid_l =3D &payld->uuid_l; + /* + * Use proper mem accessors BUT no swapping, maintain UUID + * in memory BE layout. + */ + for (int i =3D 0; i < SCMI_TLM_DE_IMPL_NUM_DWORDS; i++) + dwords[i] =3D (__force __be32)__raw_readl(&uuid_l->dwords[i]); + + /* + * Fetch BigEndian in-memory UUID fields as per SCMIv4.0 + * specification 3.12.2.2 + */ + import_uuid(&uuid->uuid, (__force const __u8 *)&dwords[0]); + + return uuid; +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_register(struct telemetry_info *ti, + struct payload __iomem *payld, + struct telemetry_uuid *uuid) +{ + if (scmi_telemetry_line_init(ti, &uuid->line, payld, TDCF_UUID_LINE)) { + kfree(uuid); + return NULL; + } + + if (scmi_telemetry_uuids_update(ti, uuid)) { + scmi_telemetry_line_put(&uuid->line); + return NULL; + } + + trace_scmi_tlm_collect(0, (__force u64)payld, 0, "SHMTI_NEW_UUID"); + + return uuid; +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_get_or_create(struct telemetry_info *ti, + struct payload __iomem *payld) +{ + struct telemetry_line *line; + struct telemetry_uuid *uuid; + + guard(mutex)(lock_for_payld(ti->payld_locks, payld)); + line =3D scmi_telemetry_line_get(ti, payld); + if (line) + return to_uuid_from_line(line); + + uuid =3D scmi_telemetry_uuid_create(ti, payld); + if (!uuid) + return uuid; + + return scmi_telemetry_uuid_register(ti, payld, uuid); +} + +static void scmi_telemetry_tdcf_uuid_parse(struct telemetry_info *ti, + struct payload __iomem *payld, + struct telemetry_shmti *shmti, + struct telemetry_uuid **active_uuid) +{ + struct telemetry_uuid *uuid, *last =3D *active_uuid; + + if (UUID_INVALID(payld)) { + trace_scmi_tlm_access(0, "UUID_INVALID", 0, 0); + return; + } + + /* A UUID descriptor MUST be returned: it is found or it is created */ + uuid =3D scmi_telemetry_uuid_get_or_create(ti, payld); + if (WARN_ON(!uuid)) + return; + + if (last) + scmi_telemetry_line_put(&last->line); + + *active_uuid =3D uuid; +} + +/** + * scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser + * + * @ti: A reference to the telemetry_info descriptor + * @payld: TDCF payld line to process + * @shmti: SHMTI descriptor inside which the scan is happening + * @active_bts: Input/output reference to keep track of the last blk_ts fo= und + * + * Process a valid TDCF BLK_TS line and, after having looked up or created= a + * blk_ts descriptor, update the related data and return it as the current= ly + * active blk_ts, given that it is effectively the last found during this + * scan. + */ +static void scmi_telemetry_tdcf_blkts_parse(struct telemetry_info *ti, + struct payload __iomem *payld, + struct telemetry_shmti *shmti, + struct telemetry_block_ts **active_bts) +{ + struct telemetry_block_ts *bts, *last =3D *active_bts; + + /* Check for spec compliance */ + if (BLK_TS_INVALID(payld)) { + trace_scmi_tlm_access(0, "BLK_TS_INVALID", 0, 0); + return; + } + + /* A BLK_TS descriptor MUST be returned: it is found or it is created */ + bts =3D scmi_telemetry_blkts_get_or_create(ti, payld); + if (WARN_ON(!bts)) + return; + + /* Update the descriptor with the lastest TS */ + scmi_telemetry_blkts_update(ti, shmti->last_magic, bts); + + if (last) + scmi_telemetry_line_put(&last->line); + + *active_bts =3D bts; +} + +static inline struct telemetry_de * +scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id, + struct payload __iomem *payld) +{ + struct telemetry_de *tde; + + tde =3D scmi_telemetry_tde_get(ti, de_id); + if (IS_ERR(tde)) + return NULL; + + tde->de.info->id =3D de_id; + tde->de.enabled =3D true; + tde->de.tstamp_enabled =3D LINE_TS_VALID(payld) || USE_BLK_TS(payld); + + if (scmi_telemetry_tde_register(ti, tde)) { + scmi_telemetry_free_tde_put(ti, tde); + return NULL; + } + + scmi_telemetry_de_state_update(ti, ENA_STATE, NULL, true); + if (tde->de.tstamp_enabled) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, NULL, true); + + return tde; +} + +static inline void +scmi_telemetry_line_data_parse(struct telemetry_de *tde, u64 *val, u64 *ts= tamp, + struct payload __iomem *payld, u32 magic) +{ + /* Data is always valid since we are NOT handling BLK TS lines here */ + *val =3D LINE_DATA_GET(&payld->l); + if (tstamp) { + if (USE_BLK_TS(payld)) { + /* Read out the actual BLK_TS */ + *tstamp =3D scmi_telemetry_blkts_read(magic, tde); + } else if (LINE_TS_VALID(payld)) { + /* + * Note that LINE_TS_VALID implies HAS_LINE_EXT and that + * the per DE line_ts_rate is advertised in the DE + * descriptor. + */ + *tstamp =3D LINE_TSTAMP_GET(&payld->tsl); + } else { + *tstamp =3D 0; + } + } + + trace_scmi_tlm_collect(tstamp ? *tstamp : 0, tde->de.info->id, + *val, "SHMTI_DE_READ"); + + scmi_telemetry_tde_cache_update(tde, *val, tstamp, &magic); +} + +static inline void scmi_telemetry_blkts_link(struct telemetry_de *tde, + struct telemetry_block_ts *bts) +{ + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (tde->bts) + return; + + guard(mutex)(&bts->line.mtx); + refcount_inc(&bts->line.users); + + /* Update TS clock rate if provided by the BLK_TS */ + if (bts->last_rate) + tde->de.info->ts_rate =3D bts->last_rate; + + tde->bts =3D bts; + trace_scmi_tlm_access(tde->de.info->id, "BLKTS_LINK", 0, 0); +} + +static inline void scmi_telemetry_uuid_link(struct telemetry_de *tde, + struct telemetry_uuid *uuid) +{ + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (tde->uuid) + return; + + guard(mutex)(&uuid->line.mtx); + refcount_inc(&uuid->line.users); + + /* Update UUID association */ + tde->uuid =3D uuid; + trace_scmi_tlm_access(tde->de.info->id, "UUID_LINK", 0, 0); +} + +/** + * scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing + * @ti: A reference to the telemetry info descriptor + * @payld: Line payload to parse + * @shmti: A reference to the containing SHMTI area + * @mode: A flag to determine the behaviour of the scan + * @active_bts: A pointer to keep track and report any found BLK timestamp= line + * @active_uuid: A pointer to keep track and report any found UUID line + * + * This routine takes care to: + * - verify line consistency in relation to the used flags and the current + * context: e.g. is there an active preceding BLK_TS line if the DataLi= ne + * sports a USE_BLKTS flag ? + * - verify the related Data Event ID exists OR create a brand new DE + * (depending on the @mode of operation) + * - links any active BLK_TS or UUID line to the current DE + * - read and save value/tstamp for the DE ONLY if anything has changed (= by + * tracking the last TDCF magic) and update related magic: this allows = to + * minimize future needs of single-DE reads + * + * Modes of operation. + * + * The scan behaviour depends on the chosen @mode: + * - SCAN_LOOKUP: the basic scan which aims to update value associated = to + * existing DEs. Any discovered DataLine that could NOT be + * matched to an existing, previously discovered, DE is + * discarded. This is the normal scan behaviour. + * - SCAN_UPDATE: a more advanced scan which provides all the SCAN_LOOK= UP + * features plus takes care to update the DEs location + * coordinates inside the SHMTI: note that the related DEs are + * still supposed to have been previously discovered when + * this scan runs. This is used to update location + * coordinates for DEs contained in a Group when such group + * is enabled. + * - SCAN_DISCOVERY: the most advanced scan available which provides all + * the SCAN_LOOKUP features plus discovery capabilities: + * any DataLine referring to a previously unknown DE leads + * to the allocation of a new DE descriptor. + * This mode is used on the first scan at init time, ONLY + * if Telemetry was found to be already enabled at boot on + * the platform side: this helps to maximize gathered + * information when dealing with out of spec firmwares. + * Any usage of this discovery mode other than in a boot-on + * enabled scenario is discouraged since it can easily + * lead to spurious DE discoveries. + */ +static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti, + struct payload __iomem *payld, + struct telemetry_shmti *shmti, + enum scan_mode mode, + struct telemetry_block_ts *active_bts, + struct telemetry_uuid *active_uuid) +{ + bool use_blk_ts =3D USE_BLK_TS(payld); + struct telemetry_de *tde; + u64 val, tstamp =3D 0; + u32 de_id; + + de_id =3D PAYLD_ID(payld); + /* Discard malformed lines...a preceding BLK_TS must exist */ + if (use_blk_ts && !active_bts) { + trace_scmi_tlm_access(de_id, "BAD_USE_BLK_TS", 0, 0); + return; + } + + /* Is this DE ID known ? */ + tde =3D scmi_telemetry_tde_lookup(ti, de_id); + if (!tde) { + if (mode !=3D SCAN_DISCOVERY) { + trace_scmi_tlm_access(de_id, "DE_INVALID", 0, 0); + return; + } + + /* In SCAN_DISCOVERY mode we allocate new DEs for unknown IDs */ + tde =3D scmi_telemetry_tde_allocate(ti, de_id, payld); + if (!tde) { + dev_err(ti->ph->dev, + "Cannot allocate TDE for ID:0x%08X\n", de_id); + return; + } + } + + /* Update DE location refs if requested: normally done only on enable */ + if (mode >=3D SCAN_UPDATE) { + guard(mutex)(&tde->mtx); + + tde->sid =3D shmti->info.sid; + tde->base =3D shmti->base; + tde->eplg =3D SHMTI_EPLG(shmti); + tde->offset =3D (void __iomem *)payld - (void __iomem *)shmti->base; + + dev_dbg(ti->ph->dev, + "TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u\n", + tde->de.info->id, tde->base, tde->offset); + } + + /* Has any value/tstamp really changed ?*/ + if (scmi_telemetry_tde_cache_unchanged(tde, shmti->last_magic)) + return; + + /* Link the related BTS when needed, it's unlinked on disable */ + if (use_blk_ts) + scmi_telemetry_blkts_link(tde, active_bts); + + /* Link the active UUID or the primary, it's unlinked on disable */ + scmi_telemetry_uuid_link(tde, active_uuid ?: ti->primary_uuid); + + /* Parse data words */ + scmi_telemetry_line_data_parse(tde, &val, &tstamp, payld, + shmti->last_magic); +} + +/** + * scmi_telemetry_tdcf_line_parse - Line parser + * @ti: A reference to the Telemetry instance + * @payld: A reference to the payload representing the line to parse + * @shmti: A reference to the descriptor of the enclosing SHMTI area + * @mode: Specification of the scanning mode + * @active_bts: A reference used to track the last BLK_TS line found + * @active_uuid: A reference used to track the last UUID line found + * + * Note that @active_bts and @active_uuid references are propagated back a= nd + * forth from the line parsers to keep track of the nearest active BLK_TS = or + * UUID line found in the SHMTI areas and, while doing that, such descript= ors + * are refcounted and tracked in the xa_lines XArray indexed by @payld. + * BLK_TS and UUID descriptors are dropped when no more linked to any DATA= line, + * during the normal system operation OR later on when the protocol is + * de-initialized following a driver removal. + * + * Return: Number of QWORDS consumed from @payld by this line parsing; note + * that buggy firmware could return bad, out of range values, so this + * value MUST be carefully checked for SHMTI boundary overflows by the + * caller. + */ +static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti, + struct payload __iomem *payld, + struct telemetry_shmti *shmti, + enum scan_mode mode, + struct telemetry_block_ts **active_bts, + struct telemetry_uuid **active_uuid) +{ + int used_qwords; + + used_qwords =3D LINE_LENGTH_QWORDS(payld); + /* Invalid lines are not an error, could simply be disabled DEs */ + if (DATA_INVALID(payld)) { + trace_scmi_tlm_access(PAYLD_ID(payld), "TDCF_INVALID", 0, 0); + return used_qwords; + } + + switch (LINE_TYPE(payld)) { + case TDCF_DATA_LINE: + scmi_telemetry_tdcf_data_parse(ti, payld, shmti, mode, + *active_bts, *active_uuid); + break; + case TDCF_BLK_TS_LINE: + scmi_telemetry_tdcf_blkts_parse(ti, payld, shmti, active_bts); + break; + case TDCF_UUID_LINE: + scmi_telemetry_tdcf_uuid_parse(ti, payld, shmti, active_uuid); + break; + default: + trace_scmi_tlm_access(PAYLD_ID(payld), "TDCF_LINE_UNKNOWN", 0, 0); + break; + } + + return used_qwords; +} + +static inline bool +scmi_telemetry_shmti_overflow(struct telemetry_shmti *shmti, void __iomem = *next) +{ + void __iomem *limit =3D shmti->base + shmti->info.len; + + /* Is payld->meta accessible ? */ + if (next >=3D limit - sizeof(u32)) + return true; + + next +=3D LINE_LENGTH_BYTES((struct payload __iomem *)next); + + return next >=3D limit; +} + +/** + * scmi_telemetry_shmti_scan - Full SHMTI scan + * @ti: A reference to the telemetry info descriptor + * @shmti_id: ID of the SHMTI area that has to be scanned + * @mode: A flag to determine the behaviour of the scan + * + * Return: 0 on Success + */ +static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, + unsigned int shmti_id, enum scan_mode mode) +{ + struct telemetry_shmti *shmti =3D &ti->shmti[shmti_id]; + struct telemetry_uuid *active_uuid =3D NULL; + struct telemetry_block_ts *active_bts =3D NULL; + struct tdcf __iomem *tdcf =3D shmti->base; + int retries =3D SCMI_TLM_TDCF_MAX_RETRIES; + u32 startm =3D TDCF_INVALID_MATCH_SEQ, endm =3D TDCF_BAD_END_SEQ; + + 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); + + /* + * Note that during a full SHMTI scan the magic seq numbers are + * checked only at the start and at the end of the scan, NOT + * between each parsed line and this has these consequences: + * - TDCF magic numbers accesses are reduced to 2 reads + * - the set of values obtained from a full scan belong all + * to the same platform update (same magic number) + * - a SHMTI full scan is an all or nothing operation: when + * a potentially corrupted read is detected along the way + * (MSEQ_MISMATCH) another full scan is triggered. + */ + startm =3D TDCF_START_SEQ_GET(tdcf); + 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 =3D startm; + + qwords =3D QWORDS(tdcf); + next =3D tdcf->payld; + while (qwords) { + int used_qwords; + + if (scmi_telemetry_shmti_overflow(shmti, next)) { + trace_scmi_tlm_access(0, "SHMTI_OVERFLOW", startm, 0); + endm =3D TDCF_INVALID_MATCH_SEQ; + goto clean; + } + + used_qwords =3D scmi_telemetry_tdcf_line_parse(ti, next, + shmti, mode, + &active_bts, + &active_uuid); + if (qwords < used_qwords) { + trace_scmi_tlm_access(PAYLD_ID(next), + "BAD_QWORDS", startm, 0); + endm =3D TDCF_INVALID_MATCH_SEQ; + goto clean; + } + + next +=3D used_qwords * 8; + qwords -=3D used_qwords; + } + + endm =3D TDCF_END_SEQ_GET(SHMTI_EPLG(shmti)); + if (startm !=3D endm) + trace_scmi_tlm_access(0, "MSEQ_MISMATCH", startm, endm); + } while (startm !=3D endm && --retries); + + /* + * Put the initial creation reference so that lines will be + * effectively dropped when there are no more data lines referencing + * them. + */ +clean: + if (active_bts) + scmi_telemetry_line_put(&active_bts->line); + if (active_uuid) + scmi_telemetry_line_put(&active_uuid->line); + + if (startm !=3D endm) { + trace_scmi_tlm_access(0, "TDCF_SCAN_FAIL", startm, endm); + return -EPROTO; + } + + return 0; +} + 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, @@ -1358,6 +2203,8 @@ static void scmi_telemetry_resources_free(void *arg) struct scmi_telemetry_res_info *rinfo; struct telemetry_info *ti =3D arg; struct scmi_telemetry_de *de; + struct telemetry_line *line; + uuid_t **cur_uuids; unsigned long idx; =20 /* Get rinfo without triggering a recursive enumeration */ @@ -1366,13 +2213,37 @@ static void scmi_telemetry_resources_free(void *arg) /* Ensure rinfo is no more accessible upfront */ smp_store_release(&ti->rinfo, NULL); =20 + /* + * Unlinking all the BLK_TS/UUID lines related to a DE triggers also + * the deallocation of such lines when the embedded refcount hits zero. + */ xa_for_each(&ti->xa_des, idx, de) { struct telemetry_de *tde =3D to_tde(de); =20 + scmi_telemetry_de_unlink(&tde->de); scmi_telemetry_free_tde_put(ti, tde); } =20 xa_destroy(&ti->xa_des); + + /* Drop reference to UUID lines kept in the growable array */ + scoped_guard(mutex, &ti->uuids_mtx) { + cur_uuids =3D rcu_dereference_protected(ti->info.uuids, + lockdep_is_held(&ti->uuids_mtx)); + for (int i =3D 0; i < ti->info.num_uuids; i++) { + struct telemetry_uuid *uuid =3D to_uuid_from_uuid_t(cur_uuids[i]); + + scmi_telemetry_line_put(&uuid->line); + } + } + + kfree_rcu_mightsleep(cur_uuids); + + /* Drop all remaining currently unbound lines and their containers */ + xa_for_each(&ti->xa_lines, idx, line) + scmi_telemetry_line_put(line); + xa_destroy(&ti->xa_lines); + kfree(ti->tdes); kfree(rinfo->des); kfree(rinfo->dei_store); @@ -1455,6 +2326,28 @@ scmi_telemetry_resources_enumerate(struct telemetry_= info *ti) return READ_ONCE(ti->rinfo); } =20 +static int scmi_telemetry_primary_uuid_init(struct telemetry_info *ti) +{ + struct telemetry_uuid *uuid; + + /* Primary UUID is stored on key 0 (NULL) */ + uuid =3D scmi_telemetry_uuid_create(ti, NULL); + if (!uuid) + return -ENOMEM; + + uuid_copy(&uuid->uuid, &ti->info.base.primary_revision); + /* Make it available in the uuid list once initialized */ + if (!scmi_telemetry_uuid_register(ti, NULL, uuid)) { + dev_err(ti->ph->dev, "Failed to register Primary UUID\n"); + return -ENOMEM; + } + + /* Ensure Primary UUID association is visible */ + smp_store_release(&ti->primary_uuid, uuid); + + return 0; +} + /** * scmi_telemetry_instance_init - Instance initializer * @ti: A reference to the telemetry info descriptor for this instance @@ -1470,6 +2363,8 @@ scmi_telemetry_resources_enumerate(struct telemetry_i= nfo *ti) */ static int scmi_telemetry_instance_init(struct telemetry_info *ti) { + unsigned int uuids_len; + uuid_t **cur_uuids; int ret; =20 /* Allocate and Initialize on first call... */ @@ -1478,11 +2373,44 @@ static int scmi_telemetry_instance_init(struct tele= metry_info *ti) return ret; =20 xa_init(&ti->xa_des); + xa_init(&ti->xa_lines); + mutex_init(&ti->lines_mtx); + for (int i =3D 0; i < 1 << PAYLD_HASH_BITS; i++) + mutex_init(&ti->payld_locks[i]); + + /* + * Always allocate at least one slot for the primary and anyway at + * least enough to avoid immediate resizing, assuring uuids_len + * always greater or equal to one. + */ + uuids_len =3D max(ti->num_shmti * 2, SCMI_UUID_DB_THRESH + 1); + cur_uuids =3D kcalloc(uuids_len, sizeof(*cur_uuids), GFP_KERNEL); + if (!cur_uuids) { + scmi_telemetry_resources_free(ti); + return -ENOMEM; + } + + mutex_init(&ti->uuids_mtx); + /* Expose new UUID pointer */ + scoped_guard(mutex, &ti->uuids_mtx) { + rcu_assign_pointer(ti->info.uuids, cur_uuids); + ti->uuids_len =3D uuids_len; + } + + ret =3D scmi_telemetry_primary_uuid_init(ti); + if (ret) { + scmi_telemetry_resources_free(ti); + return ret; + } + ret =3D devm_add_action_or_reset(ti->ph->dev, scmi_telemetry_resources_free, ti); if (ret) return ret; =20 + atomic_set(&ti->des_enabled[ENA_STATE], 0); + atomic_set(&ti->des_enabled[ENA_TSTAMP], 0); + mutex_init(&ti->des_enabled_mtx); /* Setup resources lazy initialization */ atomic_set(&ti->rinfo_initializing, 0); init_completion(&ti->rinfo_initdone); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 2850b018da0d..a6bdec1ce331 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -981,7 +981,7 @@ struct scmi_telemetry_info { struct scmi_telemetry_intervals *intervals; struct scmi_telemetry_shmti_info **shmtis; unsigned int num_uuids; - uuid_t **uuids; + uuid_t * __rcu *uuids; bool enabled; bool notif_enabled; enum scmi_telemetry_collection current_mode; --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 65E643EC699; Sun, 20 Sep 2026 09:20:45 +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=1789896051; cv=none; b=hvns+UG4bajJWMEtsoyzOUY10Z/nIGp+6pENj3UmFtOyg2vphT3dFQUFBvjt8x4OS3iGedq/KbQ8ijVI2EAUpiPb4ToO2Lnl2fe1nBiEHPM63U/V3ZNvQK8qV6C2DbBF/x3wEW5kTDcsuV37ecg6UKeQb/4fr96s8bAg4t0VrE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896051; c=relaxed/simple; bh=HTVBKOJPBscHLr1XQsTb5T8SJwho74qQxNTObkPjuNw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QcANz3RM05KrRavFOLy+q42YEZniLwaooQwsehchtBLChMpwDe7bK9WzA04fDVmqsOxGDEAE9E3U48+PrQgmU50HKb6ZwwrgqIl3BAnhasgRvwkHWnetGi4rccJCniD64Vx/LjJdQ1G36/+xjnLzDlSo+s66a8mMt/bnE6MbNKQ= 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=h4S/vwBb; 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="h4S/vwBb" 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 1E5A41CE2; Sun, 20 Sep 2026 02:20:41 -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 AE0C73F59E; Sun, 20 Sep 2026 02:20:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896044; bh=HTVBKOJPBscHLr1XQsTb5T8SJwho74qQxNTObkPjuNw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=h4S/vwBbCyUBVmd0Iv/TrWxWcsaECecygcaR9fy47+5bD6dEotxVMr/IOICI2Pv9f QW3rKGMOS1v+7pUm1BisSQYRh4DUFd7iSRHMRQnMBa2q6HyYo2GfMqpzLHJeJBaKDC MlZQ16Ay/hAnbREvFXiJTuC9t4VL3oBUjwak1lNo= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 08/25] firmware: arm_scmi: Add Telemetry configuration operations Date: Sun, 20 Sep 2026 10:19:11 +0100 Message-ID: <20260920091928.2014972-9-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 --- v11 --> v12 - added a few locking order acquisition comments - added more checks on wraparounds and alignment - added proper locking around TDE access on state_get - added a config_mtx to serialize configuration accesses between collection configure and state_set - refactored state_cache_update - added locking around sid/offset/uuid updates on enable v10 --> v11 - refactor reply processing code on DE enable/disable - use the new des_ennabled mutex to protect des cache updates - clear sid on group disable for each composing DEs - add dev_warn_once on ZERO blk_ts offset v8 --> v9 - use new scmi_telemetry_shmti_overflow() inline - review DE offsets boundary and wraparound checks (Sashiko v8 - update per-DE ts_rate for DE using BLK_TS too v7 --> v8 - Fixes for some Sashiko issues on v7 - re-add functions unused in previous patch - add proper barriers to TDE/UUID binding process - update tde->sid on SCAN_UPDATE too - check wrap-arounds on shmti_de_offset manipulation - fix GROUP enable pollutes status update (was polluting global state) - remove .all_disable telemetry_operation since unused - add WARN on if a valid SHMTI ID is reported on enabling a FastChannel/DE v6 --> v7 - expose per-DE tracked sid/offset/UUIDs - trigger a full SHMTI scan when a SHMTI-based is enabled and it still has= NO associated UUID v5 --> v6 - constify a few booleans used as inpurt in telemetry_operations - fixes (some) Sashiko reviews https://sashiko.dev/#/patchset/20260703123601.381275-1-cristian.marussi%= 40arm.com?part=3D9 - underflow on de_offs - SHMTI overflow checks - bail out on failed group scan - unlink comprising DEs on group disable - add boudnary check on message response parsing v4 --> v5 - use renamed grp_id instead of old id in group descriptors - remove useless grp_ignore param from collection_configure - fixes sparse warnings v3 --> v4 - track configured Telemetry DE states - add aggregate Telemetry state query v2 --> v3 - split from monolithic Telemetry patch - simplify clenaup with scmi_telemetry_de_unlink --- drivers/firmware/arm_scmi/telemetry.c | 592 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 17 + 2 files changed, 609 insertions(+) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index c5d17a4d9bb3..ccbd9e6dd4c5 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -508,6 +508,8 @@ struct telemetry_info { struct mutex free_mtx; struct list_head free_des; struct list_head fcs_des; + /* Protect global and per-group configuration data */ + struct mutex config_mtx; struct scmi_telemetry_info info; atomic_t rinfo_initializing; struct completion rinfo_initdone; @@ -1685,6 +1687,42 @@ static void scmi_telemetry_tdcf_uuid_parse(struct te= lemetry_info *ti, *active_uuid =3D uuid; } =20 +static struct payload __iomem * +scmi_telemetry_nearest_line_by_type(struct telemetry_shmti *shmti, + void __iomem *last, enum tdcf_line_types ltype) +{ + struct tdcf __iomem *tdcf =3D shmti->base; + void __iomem *next, *found =3D NULL; + + /* Scan from start of TDCF payloads up to last_payld */ + next =3D tdcf->payld; + while (next < last) { + if (LINE_TYPE((struct payload __iomem *)next) =3D=3D ltype) + found =3D next; + + next +=3D LINE_LENGTH_BYTES((struct payload __iomem *)next); + } + + return found; +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_bind(struct telemetry_info *ti, struct telemetry_shmt= i *shmti, + struct payload __iomem *payld, + struct payload __iomem *bts_payld) +{ + /* Trigger a manual search when no BLK_TS payload offset was provided */ + if (!bts_payld) { + /* Find the BLK_TS immediately preceding this DE payld */ + bts_payld =3D scmi_telemetry_nearest_line_by_type(shmti, payld, + TDCF_BLK_TS_LINE); + if (!bts_payld) + return NULL; + } + + return scmi_telemetry_blkts_get_or_create(ti, bts_payld); +} + /** * scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser * @@ -1814,6 +1852,19 @@ static inline void scmi_telemetry_uuid_link(struct t= elemetry_de *tde, trace_scmi_tlm_access(tde->de.info->id, "UUID_LINK", 0, 0); } =20 +static struct telemetry_uuid * +scmi_telemetry_uuid_bind(struct telemetry_info *ti, struct telemetry_shmti= *shmti, + struct payload __iomem *payld) +{ + struct payload __iomem *uuid; + + uuid =3D scmi_telemetry_nearest_line_by_type(shmti, payld, TDCF_UUID_LINE= ); + if (!uuid) + return NULL; + + return scmi_telemetry_uuid_get_or_create(ti, uuid); +} + /** * scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing * @ti: A reference to the telemetry info descriptor @@ -2095,10 +2146,550 @@ static int scmi_telemetry_shmti_scan(struct teleme= try_info *ti, return 0; } =20 +static void scmi_telemetry_group_state_update(struct telemetry_info *ti, + struct scmi_telemetry_group *grp, + const bool *enable, const 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) + scmi_telemetry_de_state_update(ti, ENA_STATE, + &de->enabled, *enable); + + if (tstamp && de->tstamp_support) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, + &de->tstamp_enabled, *tstamp); + } +} + +static void scmi_telemetry_group_disable(struct telemetry_info *ti, + struct scmi_telemetry_group *grp) +{ + struct scmi_telemetry_res_info *rinfo; + + rinfo =3D ti->res_get(ti); + /* + * Unlink the related BLK_TS/UUID lines of DEs composing the group + * and invalidate SHMTI reference. + */ + for (int i =3D 0; i < grp->info->num_des; i++) { + struct telemetry_de *tde =3D to_tde(rinfo->des[grp->des[i]]); + + scmi_telemetry_blkts_unlink(tde); + + guard(mutex)(&tde->mtx); + tde->sid =3D SHMTI_ID_INVALID; + tde->offset =3D 0; + } +} + +static inline struct payload __iomem * +scmi_telemetry_de_payld_safe_calc(struct telemetry_shmti *shmti, u32 de_of= fs) +{ + struct payload __iomem *payld; + unsigned long de_payld; + u32 de_end; + + /* Check if accessing payld->meta is safe. */ + if (check_add_overflow(de_offs, sizeof(u32), &de_end) || + de_end > shmti->info.len) + return IOMEM_ERR_PTR(-EPROTO); + + if (check_add_overflow((__force unsigned long)shmti->base, de_offs, + &de_payld)) + return IOMEM_ERR_PTR(-EPROTO); + + payld =3D shmti->base + de_offs; + /* Check line boundary */ + if (check_add_overflow(de_offs, LINE_LENGTH_BYTES(payld), &de_end) || + de_end > shmti->info.len) + return IOMEM_ERR_PTR(-EPROTO); + + return payld; +} + +static int +scmi_telemetry_tde_linked_lines_process(struct telemetry_info *ti, + struct telemetry_de *tde, + struct telemetry_shmti *shmti, + struct payload __iomem *payld, + u32 bts_offs) +{ + struct payload __iomem *bts_payld; + bool needs_primary =3D false; + + /* + * On enable if a UUID was NOT already assigned scan + * looking for possible Secondary UUID to bind: if NO + * secondary could be found embedded in the preceding + * SHMTI lines, install the primary UUID for this DE. + */ + /* Acquiring order is: tde -> line */ + scoped_guard(mutex, &tde->mtx) { + struct telemetry_uuid *uuid; + + if (!tde->uuid) { + uuid =3D scmi_telemetry_uuid_bind(ti, shmti, payld); + if (uuid) + tde->uuid =3D uuid; + else + needs_primary =3D true; + } + } + + if (needs_primary) + scmi_telemetry_uuid_link(tde, ti->primary_uuid); + + /* Process related BLK_TS lines if any */ + /* Acquiring order is: tde -> line */ + guard(mutex)(&tde->mtx); + if (!USE_BLK_TS(payld) || tde->bts) + return 0; + + bts_payld =3D NULL; + if (bts_offs && + check_add_overflow((__force unsigned long)tde->base, bts_offs, + (__force unsigned long *)&bts_payld)) + return -EPROTO; + + /* Check SHMTI overflow on returned offset */ + if (bts_payld) { + if (scmi_telemetry_shmti_overflow(shmti, bts_payld)) { + trace_scmi_tlm_access(0, "SHMTI_OVERFLOW", 0, 0); + return -EINVAL; + } + } else { + dev_warn_once(ti->ph->dev, + "Received BLK_TS with zero offset.\n"); + trace_scmi_tlm_access(0, "BLKTS_WITH_ZERO_OFFSET", 0, 0); + } + + /* Look for a viable BLK_TS line */ + tde->bts =3D scmi_telemetry_blkts_bind(ti, shmti, + payld, bts_payld); + if (WARN_ON(!tde->bts)) { + trace_scmi_tlm_access(tde->de.info->id, "INVALID_USE_BLK_TS", 0, 0); + return -EPROTO; + } + + /* Update per-DE BLK_TS if any */ + if (tde->ts_type =3D=3D TSTAMP_BLK && + tde->bts->last_rate) + tde->de.info->ts_rate =3D tde->bts->last_rate; + + return 0; +} + +static int +scmi_telemetry_state_set_tde_resp(struct telemetry_info *ti, + struct scmi_telemetry_de *de, + u32 sid, u32 de_offs, u32 bts_offs) +{ + struct telemetry_shmti *shmti; + struct payload __iomem *payld; + struct telemetry_de *tde; + + /* A FastChannel DE MUST return an INVALID SHMTI ID on enable */ + if (WARN_ON(de->fc_support)) { + trace_scmi_tlm_access(de->info->id, "INVALID_DE_FC_ON_SHMTI", 0, 0); + return -EPROTO; + } + + /* Update SHMTI/offset while skipping non-SHMTI based DES */ + shmti =3D &ti->shmti[sid]; + /* Check sanity of received location... */ + payld =3D scmi_telemetry_de_payld_safe_calc(shmti, de_offs); + if (IS_ERR(payld)) + return PTR_ERR(payld); + + tde =3D to_tde(de); + scoped_guard(mutex, &tde->mtx) { + /* ...then assign once validated */ + tde->sid =3D sid; + tde->base =3D shmti->base; + tde->offset =3D de_offs; + /* A handy reference to the Epilogue updated */ + tde->eplg =3D SHMTI_EPLG(shmti); + } + + return scmi_telemetry_tde_linked_lines_process(ti, tde, shmti, + payld, bts_offs); +} + +static int +scmi_telemetry_state_set_resp_process(struct telemetry_info *ti, void *obj, + void *r, bool is_group) +{ + struct scmi_msg_resp_telemetry_de_configure *resp =3D r; + u32 sid =3D le32_to_cpu(resp->shmti_id); + + if (IS_SHMTI_ID_VALID(sid)) { + if (sid >=3D ti->num_shmti) + return -EPROTO; + + if (!is_group) { + u32 de_offs, bts_offs; + + de_offs =3D le32_to_cpu(resp->shmti_de_offset); + bts_offs =3D le32_to_cpu(resp->blk_ts_offset); + if (!IS_ALIGNED(de_offs, 4) || !IS_ALIGNED(bts_offs, 4)) { + dev_err(ti->ph->dev, + "Returned DE/BTS offsets are NOT 32-bit aligned - de_offs:%X bts_off= s:%X\n", + de_offs, bts_offs); + return -EINVAL; + } + + return scmi_telemetry_state_set_tde_resp(ti, obj, sid, + de_offs, bts_offs); + } 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_err(ti->ph->dev, + "Failed group-scan of SHMTI ID:%d - ret:%d\n", + sid, ret); + return ret; + } + } + } else { + /* Unlink the related BLK_TS/UUID lines on disable */ + if (!is_group) { + struct scmi_telemetry_de *de =3D obj; + struct telemetry_de *tde =3D to_tde(de); + + scoped_guard(mutex, &tde->mtx) { + tde->sid =3D SHMTI_ID_INVALID; + tde->offset =3D 0; + } + + /* UUID is never unlinked once discovered */ + scmi_telemetry_blkts_unlink(tde); + } else { + scmi_telemetry_group_disable(ti, obj); + } + } + + return 0; +} + +static void +scmi_telemetry_state_cache_update(struct telemetry_info *ti, + const bool *enable, bool *enabled_state, + const bool *tstamp, bool *tstamp_enabled_state, + struct scmi_telemetry_group *grp, + struct scmi_telemetry_de *de) +{ + if (!grp && !de) + return; + + if (enable) { + if (!grp) + scmi_telemetry_de_state_update(ti, ENA_STATE, + enabled_state, + *enable); + else + *enabled_state =3D *enable; + } + + if (tstamp) { + if (!grp) { + if (de->tstamp_support) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, + tstamp_enabled_state, + *tstamp); + } else { + *tstamp_enabled_state =3D *tstamp; + } + } + + if (grp) + scmi_telemetry_group_state_update(ti, grp, enable, tstamp); +} + +static int __scmi_telemetry_state_set(const struct scmi_protocol_handle *p= h, + bool is_group, const bool *enable, + bool *enabled_state, const 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_de *de =3D !is_group ? obj : NULL; + struct scmi_telemetry_group *grp =3D is_group ? obj : NULL; + unsigned int obj_id =3D !is_group ? de->info->id : grp->info->grp_id; + struct scmi_xfer *t; + int ret; + + if (!enabled_state || !tstamp_enabled_state) + return -EINVAL; + + /* Stall concurrent configuration request */ + guard(mutex)(&ti->config_mtx); + + /* 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) { + if (de->tstamp_support) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, + tstamp_enabled_state, + *tstamp); + + return 0; + } + + 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 cpu_to_le32(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 cpu_to_le32(*tstamp ? + DE_ENABLE_WTH_TSTAMP : DE_ENABLE_NO_TSTAMP); + } else { + msg->flags |=3D cpu_to_le32(*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, obj, resp, is_group); + if (!ret) + /* Update cached state on success */ + scmi_telemetry_state_cache_update(ti, enable, enabled_state, + tstamp, tstamp_enabled_state, + grp, de); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_telemetry_state_get(const struct scmi_protocol_handle *ph, + const u32 *id, bool *enabled, bool *tstamp_enabled, + u32 *sid, u32 *offset, uuid_t *uuid) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + struct scmi_telemetry_de *de; + struct telemetry_de *tde; + + if (!enabled || !tstamp_enabled) + return -EINVAL; + + if (!id) { + guard(mutex)(&ti->des_enabled_mtx); + /* Returning the all_des_* state */ + *enabled =3D + (atomic_read(&ti->des_enabled[ENA_STATE]) =3D=3D ti->info.base.num_des); + *tstamp_enabled =3D + (atomic_read(&ti->des_enabled[ENA_TSTAMP]) =3D=3D ti->num_des_tstamp); + + return 0; + } + + de =3D xa_load(&ti->xa_des, *id); + if (!de) + return -ENODEV; + + tde =3D to_tde(de); + guard(mutex)(&tde->mtx); + + *enabled =3D de->enabled; + *tstamp_enabled =3D de->tstamp_enabled; + + if (sid) + *sid =3D tde->sid; + if (offset) + *offset =3D tde->offset; + if (uuid) { + if (tde->uuid) + uuid_copy(uuid, &tde->uuid->uuid); + else + memset(uuid, 0x0, sizeof(*uuid)); + } + + return 0; +} + +static int scmi_telemetry_state_set(const struct scmi_protocol_handle *ph, + bool is_group, u32 id, const bool *enable, + const bool *tstamp, u32 *sid, u32 *offset, + uuid_t *uuid) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + bool *enabled_state, *tstamp_enabled_state; + struct scmi_telemetry_res_info *rinfo; + void *obj; + int ret; + + 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; + } + + ret =3D __scmi_telemetry_state_set(ph, is_group, enable, enabled_state, + tstamp, tstamp_enabled_state, obj); + if (ret) + return ret; + + if (!is_group) { + struct telemetry_de *tde; + + tde =3D to_tde(obj); + guard(mutex)(&tde->mtx); + if (sid) + *sid =3D tde->sid; + if (offset) + *offset =3D tde->offset; + if (uuid) { + if (tde->uuid) + uuid_copy(uuid, &tde->uuid->uuid); + else + memset(uuid, 0x0, sizeof(*uuid)); + } + } + + return 0; +} + +static int +scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph, + unsigned int res_id, const bool *enable, + const unsigned int *update_interval_ms, + const 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; + bool tlm_enable, *current_state, is_group; + unsigned int *active_update_interval; + struct scmi_xfer *t; + __le32 interval; + u32 control; + int ret; + + /* Stall concurrent configuration request */ + guard(mutex)(&ti->config_mtx); + + if (mode && *mode =3D=3D SCMI_TLM_NOTIFICATION && + !ti->info.continuos_update_support) + return -EINVAL; + + is_group =3D res_id !=3D SCMI_TLM_GRP_INVALID; + if (is_group && res_id >=3D ti->info.base.num_groups) + return -EINVAL; + + if (!is_group) { + active_update_interval =3D &ti->info.active_update_interval; + current_mode =3D &ti->info.current_mode; + current_state =3D &ti->info.enabled; + } 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; + current_state =3D &rinfo->grps[res_id].enabled; + } + + 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 cpu_to_le32(*update_interval_ms); + + tlm_enable =3D enable ? *enable : *current_state; + next_mode =3D mode ? *mode : *current_mode; + + msg =3D t->tx.buf; + msg->grp_id =3D cpu_to_le32(res_id); + control =3D tlm_enable ? TELEMETRY_ENABLE : 0; + control |=3D !is_group ? TELEMETRY_SET_SELECTOR_ALL : TELEMETRY_SET_SELEC= TOR_GROUP; + control |=3D TELEMETRY_MODE_SET(next_mode); + msg->control =3D cpu_to_le32(control); + msg->sampling_rate =3D interval; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + *current_state =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 le32_to_cpu(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, + .collection_configure =3D scmi_telemetry_collection_configure, }; =20 /** @@ -2375,6 +2966,7 @@ static int scmi_telemetry_instance_init(struct teleme= try_info *ti) xa_init(&ti->xa_des); xa_init(&ti->xa_lines); mutex_init(&ti->lines_mtx); + mutex_init(&ti->config_mtx); for (int i =3D 0; i < 1 << PAYLD_HASH_BITS; i++) mutex_init(&ti->payld_locks[i]); =20 diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index a6bdec1ce331..b0e1e5b89e40 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -994,6 +994,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, if NULL returns the + * cumulative state of all DEs. + * @state_set: enable/disable the specific DE or GROUP with or without tim= estamps. + * @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) @@ -1002,6 +1008,17 @@ 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, + const u32 *id, bool *enabled, bool *tstamp_enabled, + u32 *sid, u32 *offset, uuid_t *uuid); + int (*state_set)(const struct scmi_protocol_handle *ph, + bool is_group, u32 id, const bool *enable, + const bool *tstamp, u32 *sid, u32 *offset, + uuid_t *uuid); + int (*collection_configure)(const struct scmi_protocol_handle *ph, + unsigned int res_id, const bool *enable, + const unsigned int *update_interval_ms, + const enum scmi_telemetry_collection *mode); }; =20 /** --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id AEBFF3CB57D; Sun, 20 Sep 2026 09:20:50 +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=1789896056; cv=none; b=rDuBlGuRsNKhEP2S/Ld5qMgi03UI53POiVXuX8B6XPfXDEvxqyVyozlCVJ/1jHKTB55Eq/4oHJKP8XsqdRmblO2CG/tIwWzgO3xwGrB2lCCfun7OEa01jEl1Sew6fI5tvq9KYCSz7xRn47S4gK6Zz271hlE7jQzoNGjuLCevBz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896056; c=relaxed/simple; bh=rQuSTGD8MiyszDiMcpLRZKZUDiubRtv8oiTOglgxROM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ARMUCd+YNpbg/nCz6gCcAol3Im32wfpc3tnITSt4rxyv/aTyiewaZzXPTqXw/xqNzX8zDCNtdwsBNulpPEFfqpuq9xzd7yZTO4uXPlK228/uSUVqUyH4MpX53hg+5VgAyHDQhPoghGw1xO1Cq2DEDod4CXmYMu1u2pnDw63lJFM= 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=RbBVbVr0; 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="RbBVbVr0" 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 67B381D15; Sun, 20 Sep 2026 02:20:45 -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 059793F59E; Sun, 20 Sep 2026 02:20:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896048; bh=rQuSTGD8MiyszDiMcpLRZKZUDiubRtv8oiTOglgxROM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RbBVbVr0YSKMOUaFY3TRhMgyvH2ghi9oSLn2kx0Yj8B5D0Cf2fEbq18xagYuQ3YBd VsmYvg/t47qdOFj9x3DnTyOW7/b4UKiw99o9s/cX4Be18RqRkEt5T0LxxkBe+dVBjA fq0iOtY9BWTi0+0Fny6ELAJr9E5KdqBLGPCQpPck= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 09/25] firmware: arm_scmi: Add Telemetry DataEvent read capabilities Date: Sun, 20 Sep 2026 10:19:12 +0100 Message-ID: <20260920091928.2014972-10-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 Telemetry operations needed to read DataEvent values and timestamps as single entities or all together in a single bulk buffer. The returned values are effectiely retrieved from the platform only when strictly needed, i.e. when no fresh recent cached value was already available. The DataEvent values are fetched transparently from the platform origins using the proper synchronization and consistency primitives, directly from the SHMTIs areas or the FastChannels memory depending on the configuration. Signed-off-by: Cristian Marussi --- v11 --> v12 - using new TDCF_INVALID_MATCH_SEQ macro - reporting transient DATA_INVALID lines as -EINVAL - added cacheable param to msg_payld_process v10 --> v11 - split out and relocate unrelated code to previous patches - add TDE mutex_lock when getting payload address - check LINE_TYPE while parsing payloads - check overflows and boundary on single sample replies v8 -->v9 - Fix Sashiko reports for v8 - check if TS supported on FC line before using it - check for unexpected shorter reply on sample_get - use non-LE accessors in msg_payld where appropriate v7 --> v8 - Fixes for some Sashiko issues on v7 - added CACHED_DE_READ trace - reworked locking around line handling: blkts/uuid v6 -->v7 - usse proper le32 accesors, fix sparse warns v5 --> v6 - fixed some Sashiko review - add new macros parse TDCF-like content on regular ram (non IO) https://sashiko.dev/#/patchset/20260703123601.381275-1-cristian.marussi%40= arm.com?part=3D10 - fix retry loop in DE parse to avoid exiting on transient errors - use proper non-IO accessors for msg payld parsing - add msg payld boundary check v4 --> v5 - removed UAPI structures dependency - cleaned up sample_get logic around groups - fixes sparse warnings v3 --> v4 - fix typos in comments - extend TDE cache helpers with cache lookup routine and use cumulative scmi_telemetry_de_sample param v2 --> v3 - split from monolithic Telemetry patch - simplity using a few assignement using ternary ops - remove useless ts param from scanning function - use a compound literal to simplify samples init - add a missing __must_check on telemetry_ops - changed errno on DE read troubles: - ENODEV/ENOENT: DE is UNKNOWN - EINVAL: DE is marked as DATA_INVALID - ENODATA: TLM susbsystem or the specific DE is OFF --- drivers/firmware/arm_scmi/telemetry.c | 516 ++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 24 ++ 2 files changed, 511 insertions(+), 29 deletions(-) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index ccbd9e6dd4c5..c4981d3f5457 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -651,16 +651,37 @@ scmi_telemetry_tde_cache_unchanged(struct telemetry_d= e *tde, u32 magic) } =20 static void -scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val, - u64 *tstamp, u32 *magic) +scmi_telemetry_tde_cache_update(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample, + u32 *magic) { guard(mutex)(&tde->mtx); =20 tde->last_magic =3D magic ? *magic : TDCF_BAD_END_SEQ; - tde->last_val =3D val; - tde->last_ts =3D tstamp && scmi_tde_has_tstamp(tde) ? *tstamp : 0; - if (tstamp) - *tstamp =3D tde->last_ts; + tde->last_val =3D sample->val; + tde->last_ts =3D scmi_tde_has_tstamp(tde) ? sample->tstamp : 0; + /* Update returned value too */ + sample->tstamp =3D tde->last_ts; +} + +static int +scmi_telemetry_tde_cache_lookup(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample, + u32 *magic) +{ + guard(mutex)(&tde->mtx); + + if (magic && tde->last_magic !=3D *magic) + return -EAGAIN; + + sample->id =3D tde->de.info->id; + sample->val =3D tde->last_val; + sample->tstamp =3D tde->last_ts; + + trace_scmi_tlm_collect(sample->tstamp, sample->id, + sample->val, "CACHED_DE_READ"); + + return 0; } =20 struct scmi_tlm_de_priv { @@ -1790,31 +1811,31 @@ scmi_telemetry_tde_allocate(struct telemetry_info *= ti, u32 de_id, } =20 static inline void -scmi_telemetry_line_data_parse(struct telemetry_de *tde, u64 *val, u64 *ts= tamp, +scmi_telemetry_line_data_parse(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample, struct payload __iomem *payld, u32 magic) { /* Data is always valid since we are NOT handling BLK TS lines here */ - *val =3D LINE_DATA_GET(&payld->l); - if (tstamp) { - if (USE_BLK_TS(payld)) { - /* Read out the actual BLK_TS */ - *tstamp =3D scmi_telemetry_blkts_read(magic, tde); - } else if (LINE_TS_VALID(payld)) { - /* - * Note that LINE_TS_VALID implies HAS_LINE_EXT and that - * the per DE line_ts_rate is advertised in the DE - * descriptor. - */ - *tstamp =3D LINE_TSTAMP_GET(&payld->tsl); - } else { - *tstamp =3D 0; - } + sample->val =3D LINE_DATA_GET(&payld->l); + if (USE_BLK_TS(payld)) { + /* Read out the actual BLK_TS */ + sample->tstamp =3D scmi_telemetry_blkts_read(magic, tde); + } else if (LINE_TS_VALID(payld)) { + /* + * Note that LINE_TS_VALID implies HAS_LINE_EXT and that + * the per DE line_ts_rate is advertised in the DE + * descriptor. + */ + sample->tstamp =3D LINE_TSTAMP_GET(&payld->tsl); + } else { + sample->tstamp =3D 0; } =20 - trace_scmi_tlm_collect(tstamp ? *tstamp : 0, tde->de.info->id, - *val, "SHMTI_DE_READ"); + /* Trace originally read tstamp */ + trace_scmi_tlm_collect(sample->tstamp, tde->de.info->id, + sample->val, "SHMTI_DE_READ"); =20 - scmi_telemetry_tde_cache_update(tde, *val, tstamp, &magic); + scmi_telemetry_tde_cache_update(tde, sample, &magic); } =20 static inline void scmi_telemetry_blkts_link(struct telemetry_de *tde, @@ -1918,9 +1939,9 @@ static void scmi_telemetry_tdcf_data_parse(struct tel= emetry_info *ti, struct telemetry_block_ts *active_bts, struct telemetry_uuid *active_uuid) { + struct scmi_telemetry_de_sample sample =3D {}; bool use_blk_ts =3D USE_BLK_TS(payld); struct telemetry_de *tde; - u64 val, tstamp =3D 0; u32 de_id; =20 de_id =3D PAYLD_ID(payld); @@ -1972,9 +1993,8 @@ static void scmi_telemetry_tdcf_data_parse(struct tel= emetry_info *ti, /* Link the active UUID or the primary, it's unlinked on disable */ scmi_telemetry_uuid_link(tde, active_uuid ?: ti->primary_uuid); =20 - /* Parse data words */ - scmi_telemetry_line_data_parse(tde, &val, &tstamp, payld, - shmti->last_magic); + /* Parse data words ... only for the sake of updating TDE cache */ + scmi_telemetry_line_data_parse(tde, &sample, payld, shmti->last_magic); } =20 /** @@ -2683,6 +2703,441 @@ scmi_telemetry_collection_configure(const struct sc= mi_protocol_handle *ph, return ret; } =20 +static inline void +scmi_telemetry_de_data_fc_read(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample) +{ + struct fc_tsline __iomem *fc =3D tde->base + tde->offset; + + sample->val =3D LINE_DATA_GET(fc); + sample->tstamp =3D tde->ts_type =3D=3D TSTAMP_LINE ? LINE_TSTAMP_GET(fc) = : 0; + + /* Trace originally read tstamp */ + trace_scmi_tlm_collect(sample->tstamp, tde->de.info->id, sample->val, + "FC_READ"); + + scmi_telemetry_tde_cache_update(tde, sample, NULL); +} + +static void scmi_telemetry_scan_update(struct telemetry_info *ti) +{ + struct telemetry_de *tde; + + /* Scan all SHMTIs ... */ + for (int id =3D 0; id < ti->num_shmti; id++) { + int ret; + + ret =3D scmi_telemetry_shmti_scan(ti, id, SCAN_LOOKUP); + if (ret) + dev_warn(ti->ph->dev, + "Failed update-scan of SHMTI ID:%d - ret:%d\n", + id, ret); + } + + if (!ti->info.fc_support) + return; + + /* Need to enumerate resources to access fastchannels */ + ti->res_get(ti); + list_for_each_entry(tde, &ti->fcs_des, item) { + struct scmi_telemetry_de_sample sample =3D {}; + + if (!tde->de.enabled) + continue; + + /* Only for the sake of updating TDE cache */ + scmi_telemetry_de_data_fc_read(tde, &sample); + } +} + +/* + * TDCF and TS Line Management Notes + * --------------------------------- + * + * TDCF Payload Metadata notable bits: + * - Bit[3]: USE BLK Tstamp + * - Bit[2]: Line-Extension Field present (LineTstamp) + * - Bit[1]: Tstamp VALID + * - Bit[0]: Data INVALID + * + * CASE_1: + * ------- + * + A DE is enabled with timestamp disabled, so the TS fields are + * NOT present + * -> BIT[3:0] =3D 0000b + * + * - 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:0] =3D 0110b + * + * - 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 clk rate) + * 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 (with same clk rate) + * 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] =3D 1010b + * + * + 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) + * anyway it will be marked as: + * -> BIT[3:0] =3D 0101b iff it was a LINE_TSTAMP + * -> BIT[3:0] =3D 0001b iff it was a BLK_TSTAMP + * + * CASE_2: + * ------- + * + A DE is enabled with LINE timestamp enabled, so the TS_Line is there + * -> BIT[3:0] =3D 0110b + * + 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 physically there but NOT valid + * -> BIT[3:0] =3D 0100b + * + the hole from the timestamp remain there unused until + * - you enable again the TS so the hole is used again + * -> BIT[3:0] =3D 0110b + * 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 struct payload __iomem * +scmi_telemetry_tdcf_de_payld_get(struct telemetry_de *tde) +{ + struct payload __iomem *payld; + + guard(mutex)(&tde->mtx); + payld =3D tde->base + tde->offset; + if (DATA_INVALID(payld)) { + trace_scmi_tlm_access(PAYLD_ID(payld), "DE_DATA_INVALID", 0, 0); + return IOMEM_ERR_PTR(-EINVAL); + } + + if (IS_BLK_TS_LINE(payld)) { + trace_scmi_tlm_access(tde->de.info->id, "BAD_DE_META", 0, 0); + return IOMEM_ERR_PTR(-EPROTO); + } + + if (PAYLD_ID(payld) !=3D tde->de.info->id) { + trace_scmi_tlm_access(tde->de.info->id, "DE_ID_MISMATCH", 0, 0); + return IOMEM_ERR_PTR(-ENODEV); + } + + /* + * A valid line using BLK_TS should have been initialized with the + * related BLK_TS when enabled. + */ + if (WARN_ON((USE_BLK_TS(payld) && !tde->bts))) { + trace_scmi_tlm_access(tde->de.info->id, "BAD_USE_BLK_TS", 0, 0); + return IOMEM_ERR_PTR(-EPROTO); + } + + return payld; +} + +static int +scmi_telemetry_tdcf_de_parse(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample) +{ + int retries =3D SCMI_TLM_TDCF_MAX_RETRIES; + struct tdcf __iomem *tdcf =3D tde->base; + u32 startm =3D TDCF_INVALID_MATCH_SEQ, endm =3D TDCF_BAD_END_SEQ; + int err =3D 0; + + 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 =3D TDCF_START_SEQ_GET(tdcf); + if (IS_BAD_START_SEQ(startm)) { + trace_scmi_tlm_access(tde->de.info->id, "MSEQ_BADSTART", + startm, 0); + continue; + } + + /* Return last readings if nothing changed */ + err =3D scmi_telemetry_tde_cache_lookup(tde, sample, &startm); + if (!err) + return 0; + + /* + * Any -EINVAL error ? + * In case of -EINVAL errors retry and then report the error, as + * usual, only when retries have been exhausted; these errors + * derive from DATA_INVALID flags applied by the platform due to + * possibly transient errors: when catched here, they are NOT + * simply due to the DE having been disabled, where the platform + * equally set DATA_INVALID, since that condition is catched + * earlier along the read path. + */ + payld =3D scmi_telemetry_tdcf_de_payld_get(tde); + err =3D PTR_ERR_OR_ZERO(payld); + if (!err) + scmi_telemetry_line_data_parse(tde, sample, payld, startm); + else if (err !=3D -EINVAL) + return err; + + endm =3D TDCF_END_SEQ_GET(tde->eplg); + if (startm !=3D endm) + trace_scmi_tlm_access(tde->de.info->id, "MSEQ_MISMATCH", + startm, endm); + } while (startm !=3D endm && --retries); + + if (startm !=3D endm) { + trace_scmi_tlm_access(tde->de.info->id, "TDCF_DE_FAIL", + startm, endm); + return -EPROTO; + } + + return err; +} + +static int scmi_telemetry_de_access(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample) +{ + if (!tde->de.fc_support) + return scmi_telemetry_tdcf_de_parse(tde, sample); + + scmi_telemetry_de_data_fc_read(tde, sample); + + return 0; +} + +static int scmi_telemetry_de_collect(struct telemetry_info *ti, + struct scmi_telemetry_de *de, + struct scmi_telemetry_de_sample *sample) +{ + struct telemetry_de *tde =3D to_tde(de); + + if (!de->enabled) + return -ENODATA; + + /* + * DE readings returns cached values when: + * - DE data value was retrieved via notification/delayed_response + */ + if (tde->cached_msg) + return scmi_telemetry_tde_cache_lookup(tde, sample, NULL); + + return scmi_telemetry_de_access(tde, sample); +} + +static int scmi_telemetry_de_data_read(const struct scmi_protocol_handle *= ph, + struct scmi_telemetry_de_sample *sample) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + struct scmi_telemetry_de *de; + + if (!ti->info.enabled || !sample) + return -ENODATA; + + de =3D xa_load(&ti->xa_des, sample->id); + if (!de) + return -ENOENT; + + return scmi_telemetry_de_collect(ti, de, sample); +} + +static int +scmi_telemetry_samples_collect(struct telemetry_info *ti, int grp_id, + int *num_samples, + struct scmi_telemetry_de_sample *samples) +{ + struct scmi_telemetry_res_info *rinfo; + int max_samples; + + max_samples =3D *num_samples; + *num_samples =3D 0; + + rinfo =3D ti->res_get(ti); + for (int i =3D 0; i < rinfo->num_des; i++) { + struct scmi_telemetry_de *de; + + de =3D rinfo->des[i]; + /* Skip disabled DEs and not belonging to the selected Group */ + if (!de->enabled || + (grp_id !=3D SCMI_TLM_GRP_INVALID && + (!de->grp || de->grp->info->grp_id !=3D grp_id))) + continue; + + /* Must be checked here to skip disabled and non selected */ + if (*num_samples =3D=3D max_samples) + return -ENOSPC; + + /* Note that this cannot fail when passing a NULL magic */ + scmi_telemetry_tde_cache_lookup(to_tde(de), + &samples[(*num_samples)++], NULL); + } + + 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 =3D ph->get_priv(ph); + + if (!ti->info.enabled || !num_samples || !samples) + return -EINVAL; + + /* Trigger a full SHMTIs & FCs scan */ + scmi_telemetry_scan_update(ti); + + /* 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, __le32 *dwords, + size_t payld_bytes, bool cacheable) +{ + struct scmi_telemetry_res_info *rinfo; + u32 next =3D 0; + + rinfo =3D ti->res_get(ti); + if (!rinfo->fully_enumerated) { + dev_warn_once(ti->ph->dev, + "Cannot process DEs in message payload. Drop.\n"); + return; + } + + while (next < num_dwords) { + struct payload *payld =3D (struct payload *)&dwords[next]; + struct scmi_telemetry_de *de; + struct telemetry_de *tde; + u32 de_id, meta =3D le32_to_cpu(payld->meta); + unsigned int wlen; + + wlen =3D LINE_LENGTH_WORDS_RAW(meta); + /* Check Boundary */ + if (next + wlen > payld_bytes / sizeof(u32)) + return; + + next +=3D wlen; + de_id =3D le32_to_cpu(payld->id); + if (DATA_INVALID_RAW(meta)) { + trace_scmi_tlm_access(de_id, "MSG_INVALID", 0, 0); + continue; + } + + if (LINE_TYPE_RAW(meta) !=3D TDCF_DATA_LINE) { + trace_scmi_tlm_access(de_id, "MSG_BAD_TYPE", 0, 0); + continue; + } + + if (USE_BLK_TS_RAW(meta)) { + trace_scmi_tlm_access(de_id, "MSG_BAD_USE_BLKTS", 0, 0); + continue; + } + + de =3D xa_load(&ti->xa_des, de_id); + if (!de || !de->enabled) { + dev_err(ti->ph->dev, + "MSG - Received INVALID DE - ID:%u enabled:%c\n", + de_id, de ? (de->enabled ? 'Y' : 'N') : 'X'); + continue; + } + + tde =3D to_tde(de); + guard(mutex)(&tde->mtx); + tde->cached_msg =3D true; + tde->last_val =3D LINE_DATA_GET_RAW(&payld->tsl); + /* TODO BLK_TS in notification payloads */ + tde->last_ts =3D HAS_LINE_EXT_RAW(meta) && + LINE_TS_VALID_RAW(meta) ? + LINE_TSTAMP_GET_RAW(&payld->tsl) : 0; + + trace_scmi_tlm_collect(tde->last_ts, tde->de.info->id, + tde->last_val, "MESSAGE"); + } +} + +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 =3D ph->get_priv(ph); + struct scmi_msg_telemetry_config_set *msg; + struct scmi_xfer *t; + bool is_group; + u32 control; + int ret; + + if (!ti->info.enabled || !num_samples || !samples) + return -EINVAL; + + is_group =3D grp_id !=3D SCMI_TLM_GRP_INVALID; + if (is_group && grp_id >=3D ti->info.base.num_groups) + return -EINVAL; + + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_SET, + sizeof(*msg), 0, &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->grp_id =3D cpu_to_le32(grp_id); + control =3D TELEMETRY_ENABLE; + control |=3D !is_group ? TELEMETRY_SET_SELECTOR_ALL : + TELEMETRY_SET_SELECTOR_GROUP; + control |=3D TELEMETRY_MODE_SINGLE; + msg->control =3D cpu_to_le32(control); + msg->sampling_rate =3D 0; + + ret =3D ph->xops->do_xfer_with_response(ph, t); + if (!ret) { + struct scmi_msg_resp_telemetry_reading_complete *r =3D t->rx.buf; + u32 num_dwords =3D le32_to_cpu(r->num_dwords); + + /* Update cached DEs values from payload */ + if (num_dwords) { + size_t payld_sz =3D 0, dwords_sz; + + if (check_mul_overflow(num_dwords, sizeof(r->dwords[0]), &dwords_sz) || + check_add_overflow(sizeof(*r), dwords_sz, &payld_sz) || + payld_sz > t->rx.len) { + dev_err(ph->dev, + "Malformed Telemetry delayed response - num_dwords:%u payld_sz:%zd = rx_len:%zd\n", + num_dwords, payld_sz, t->rx.len); + ret =3D -EPROTO; + goto out; + } + + scmi_telemetry_msg_payld_process(ti, num_dwords, r->dwords, + payld_sz - sizeof(*r), false); + } + + /* Scan and update SMHTIs and FCs */ + scmi_telemetry_scan_update(ti); + + /* Collect all last cached values */ + ret =3D scmi_telemetry_samples_collect(ti, grp_id, num_samples, + samples); + } + +out: + 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, @@ -2690,6 +3145,9 @@ static const struct scmi_telemetry_proto_ops tlm_prot= o_ops =3D { .state_get =3D scmi_telemetry_state_get, .state_set =3D scmi_telemetry_state_set, .collection_configure =3D scmi_telemetry_collection_configure, + .de_data_read =3D scmi_telemetry_de_data_read, + .des_bulk_read =3D scmi_telemetry_des_bulk_read, + .des_sample_get =3D scmi_telemetry_des_sample_get, }; =20 /** diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index b0e1e5b89e40..c401d04758c5 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -987,6 +987,13 @@ struct scmi_telemetry_info { enum scmi_telemetry_collection current_mode; }; =20 +struct scmi_telemetry_de_sample { + unsigned int id; + unsigned int pad; + unsigned long long tstamp; + unsigned long long val; +}; + /** * struct scmi_telemetry_proto_ops - represents the various operations pro= vided * by SCMI Telemetry Protocol @@ -1000,6 +1007,15 @@ struct scmi_telemetry_info { * @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. + * @de_data_read: on-demand read of a single DE and related optional times= tamp: + * 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 ju= st + * the ones belonging to a specific group when provided. + * This causes an immediate update platform-side of all the + * enabled DEs. */ struct scmi_telemetry_proto_ops { const struct scmi_telemetry_info __must_check *(*info_get) @@ -1019,6 +1035,14 @@ struct scmi_telemetry_proto_ops { unsigned int res_id, const bool *enable, const unsigned int *update_interval_ms, const enum scmi_telemetry_collection *mode); + int __must_check (*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); }; =20 /** --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8B59731E846; Sun, 20 Sep 2026 09:20:55 +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=1789896060; cv=none; b=mSMuBi7DVRQsM8k4/QOJNLzpxwOCrXLX7MswdXBgeiF9uSXsRROJLw7Mp3a1ZBlVjphjQkFOIHVSRSRSaEP/qdOM3Ev0+Z9816JKn66YUEVmer2fnnTP2XU0RseMMaFY0WdeZXgaZddLMi0oPTQNHYzYHcRVDfv8K0gD1oV7LHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896060; c=relaxed/simple; bh=XFBCgnqtRCtsLLlDGLuFPJMX0AdnyAny6gw768e5STU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OPUSfrWivYqVoyRbTeHZVmJiMt/h3o7UevPvfdGxjsaXtcmcBvCk1a8yp+a5po/xEp3P+6gUelkBgbvVOD5mH99xd+BipeksCz2tC1+hYsl+siLCkBdUIAgxyxL/MvByzA39If4/Mrq9WGvxFh+F3e6wjpWY3isNGmhPwO6tGyU= 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=G2bKRYaH; 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="G2bKRYaH" 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 A95CE1D31; Sun, 20 Sep 2026 02:20:49 -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 4C2293F59E; Sun, 20 Sep 2026 02:20:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896053; bh=XFBCgnqtRCtsLLlDGLuFPJMX0AdnyAny6gw768e5STU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=G2bKRYaHkyLgYM78pm30BT1qkUU0Lrj/GEYo0ODKIRITMwECzlQpqnt+ep9p4n38D bKMdmXvVOFv4OvxbOUtL7HcIOc1bobqGS8NYrLPPTKBa/EPdsr4roQ7oUp5yrTXm3Z hhhn8SDwotPq4Duo7K+xTcHFyImzW4NbucqilVJE= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 10/25] firmware: arm_scmi: Add support for Telemetry reset Date: Sun, 20 Sep 2026 10:19:13 +0100 Message-ID: <20260920091928.2014972-11-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 Telemetry operations needed to request platform to reset telemetry current configuration and data events values. Signed-off-by: Cristian Marussi --- v11 --> v12 - using config_mtx to sync with ongoing configurations - use proper helpers to update cache state v10 --> v11 - protect TDE updates on reset - use des_enable mutex when clearing des cache - zero also active_update_interval v8 --> v9 - add a commment on why NOT unlinking UUID v6 --> v7 - add proper cleanup of sid/offs/uuids and caches on RESET v5 --> v6 - added missing reset of global state v3 --> v4 - reset tracked Telemetry DE state v2 --> v3 - split from monolithic Telemetry patch - use scmi_telemetry_de_unlink --- drivers/firmware/arm_scmi/telemetry.c | 80 +++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 2 + 2 files changed, 82 insertions(+) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index c4981d3f5457..3e0c3c42e5a7 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -3138,6 +3138,85 @@ static int scmi_telemetry_des_sample_get(const struc= t scmi_protocol_handle *ph, return ret; } =20 +static void scmi_telemetry_local_resources_reset(struct telemetry_info *ti) +{ + struct scmi_telemetry_res_info *rinfo; + + /* Stall concurrent configuration request */ + guard(mutex)(&ti->config_mtx); + + ti->info.enabled =3D false; + ti->info.notif_enabled =3D false; + ti->info.current_mode =3D SCMI_TLM_ONDEMAND; + ti->info.active_update_interval =3D 0; + + /* Get rinfo as it is...without triggering an enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + /* Clear all local state...*/ + for (int i =3D 0; i < rinfo->num_des; i++) { + struct telemetry_de *tde =3D to_tde(rinfo->des[i]); + + scoped_guard(mutex, &tde->mtx) { + tde->last_ts =3D 0; + tde->last_val =3D 0; + tde->last_magic =3D 0; + if (!tde->de.fc_support) { + tde->sid =3D SHMTI_ID_INVALID; + tde->base =3D NULL; + tde->offset =3D 0; + tde->eplg =3D NULL; + } + + scmi_telemetry_de_state_update(ti, ENA_STATE, + &tde->de.enabled, false); + + if (tde->de.tstamp_support) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, + &tde->de.tstamp_enabled, + false); + } + + /* + * Unlinking ONLY BLK_TS lines since, per the SCMI spec, the + * UUID/DE associations are permanent within the same session + * boot: after a reset has cleared the server-side configuration + * the next time SHMTIs will be re-populated, these same UUID/DE + * associations will be re-instated. + */ + scmi_telemetry_blkts_unlink(tde); + } + + for (int i =3D 0; i < rinfo->num_groups; i++) { + rinfo->grps[i].enabled =3D false; + rinfo->grps[i].tstamp_enabled =3D false; + rinfo->grps[i].current_mode =3D SCMI_TLM_ONDEMAND; + rinfo->grps[i].active_update_interval =3D 0; + } +} + +static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph) +{ + struct scmi_xfer *t; + int ret; + + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_RESET, sizeof(u32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(0, t->tx.buf); + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + struct telemetry_info *ti =3D ph->get_priv(ph); + + scmi_telemetry_local_resources_reset(ti); + } + + 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, @@ -3148,6 +3227,7 @@ static const struct scmi_telemetry_proto_ops tlm_prot= o_ops =3D { .de_data_read =3D scmi_telemetry_de_data_read, .des_bulk_read =3D scmi_telemetry_des_bulk_read, .des_sample_get =3D scmi_telemetry_des_sample_get, + .reset =3D scmi_telemetry_reset, }; =20 /** diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index c401d04758c5..5a2787b30120 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1016,6 +1016,7 @@ struct scmi_telemetry_de_sample { * the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. + * @reset: reset configuration and telemetry data. */ struct scmi_telemetry_proto_ops { const struct scmi_telemetry_info __must_check *(*info_get) @@ -1043,6 +1044,7 @@ struct scmi_telemetry_proto_ops { 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 (*reset)(const struct scmi_protocol_handle *ph); }; =20 /** --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 34DCF3EC81C; Sun, 20 Sep 2026 09:20:58 +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=1789896062; cv=none; b=qFNRCf14hbbFeFR5xopQwCnBlou/pDJwj1/H9JpmgFqnX4yH/I6m7S+XVAwZ0DJLIhRzFRcW1NqFBcVBzCFiRPTI9WTWSMs+upiFWBjlHcs8lh/Bv3mnvHeudcDU6N9Zn4r4yZlAUD6iACs3QUARqeJ1mYlp48HzluEwovGFP8g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896062; c=relaxed/simple; bh=N5kKCJ9jGjZppJ40wiinVsxp9Zskb71HOdqUMG+h/9k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Q10EDD3a8wVYFCjYTvUUjLSKuzLER28aW5ob1sfJ9EaKISP2V4/VgsHhCq8kBm1XsNR3sFgDJ+PgDG5wmt6aXBEUKzaE62mdD6xOrGQjUP1HVvYHzNmzTIIj0PIvVmHH4wykMtaF+BTuv5GdP2ZMsygjslT+t5Bp8jpjQVa0S20= 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=V5+2EIIF; 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="V5+2EIIF" 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 D7EF21D34; Sun, 20 Sep 2026 02:20:53 -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 8CE683F59E; Sun, 20 Sep 2026 02:20:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896057; bh=N5kKCJ9jGjZppJ40wiinVsxp9Zskb71HOdqUMG+h/9k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V5+2EIIFQagrB2KL9sjDM2EnkaibNzhfFzLJLmqYPvjzJhIwoFeXLDguqRpKFzDPQ VyQZrt/7Z5LIXDd+F2+kE/KFtBpaZGq9PIaEKZmLE7mCMLWlJ43LxzcJl8FnDDSrb5 FwV/UkrPTXE9UWX3HMtM2ly/Um8OUd8VvLb8izIc= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 11/25] firmware: arm_scmi: Add Telemetry notification support Date: Sun, 20 Sep 2026 10:19:14 +0100 Message-ID: <20260920091928.2014972-12-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 notifications to Telemetry protocol and register an internal notifier during protocol initialization: any DE value received inside a notification payload will be cached for future user consumption. Signed-off-by: Cristian Marussi --- v11 --> v12 - marking notifs TDE as cacheable - updating cache within TDE lock when processing msg_payld v10 --> v11 - link DE received by notif to the primary UUID when first seen v8 --> v9 - fix notif boundary and wraparound checks - rollback notif report payload in __le32 format before parsing to align with single read payload parsing v6 --> v7 - fix sparse warn on notof payload handling v5 -> v6 - use protocol instance notifier interface - use new RAW macros to access payload - add proper boundary checks on notification payload - fix mutex scope in msg payld parsing - add notification payload consistency check (Sahiko) v3 -> v4 - use TDE cache to save DEs received via msg payload v2 --> v3 - changed a few dev_err into traces - split from monolithic telemetry protocol patch - use memcpy_from_le32 --- drivers/firmware/arm_scmi/telemetry.c | 192 +++++++++++++++++++++++--- include/linux/scmi_protocol.h | 9 ++ 2 files changed, 183 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index 3e0c3c42e5a7..dde643f5de4d 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -511,6 +511,7 @@ struct telemetry_info { /* Protect global and per-group configuration data */ struct mutex config_mtx; struct scmi_telemetry_info info; + struct notifier_block telemetry_nb; atomic_t rinfo_initializing; struct completion rinfo_initdone; #define PAYLD_HASH_BITS 4 @@ -519,6 +520,9 @@ struct telemetry_info { struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); }; =20 +#define telemetry_nb_to_info(x) \ + container_of(x, struct telemetry_info, telemetry_nb) + static struct scmi_telemetry_res_info * __scmi_telemetry_resources_get(struct telemetry_info *ti); =20 @@ -651,12 +655,10 @@ scmi_telemetry_tde_cache_unchanged(struct telemetry_d= e *tde, u32 magic) } =20 static void -scmi_telemetry_tde_cache_update(struct telemetry_de *tde, - struct scmi_telemetry_de_sample *sample, - u32 *magic) +__scmi_telemetry_tde_cache_update(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample, + u32 *magic) { - guard(mutex)(&tde->mtx); - tde->last_magic =3D magic ? *magic : TDCF_BAD_END_SEQ; tde->last_val =3D sample->val; tde->last_ts =3D scmi_tde_has_tstamp(tde) ? sample->tstamp : 0; @@ -664,6 +666,15 @@ scmi_telemetry_tde_cache_update(struct telemetry_de *t= de, sample->tstamp =3D tde->last_ts; } =20 +static void +scmi_telemetry_tde_cache_update(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample, + u32 *magic) +{ + guard(mutex)(&tde->mtx); + __scmi_telemetry_tde_cache_update(tde, sample, magic); +} + static int scmi_telemetry_tde_cache_lookup(struct telemetry_de *tde, struct scmi_telemetry_de_sample *sample, @@ -3020,6 +3031,7 @@ scmi_telemetry_msg_payld_process(struct telemetry_inf= o *ti, =20 while (next < num_dwords) { struct payload *payld =3D (struct payload *)&dwords[next]; + struct scmi_telemetry_de_sample sample =3D {}; struct scmi_telemetry_de *de; struct telemetry_de *tde; u32 de_id, meta =3D le32_to_cpu(payld->meta); @@ -3049,23 +3061,26 @@ scmi_telemetry_msg_payld_process(struct telemetry_i= nfo *ti, =20 de =3D xa_load(&ti->xa_des, de_id); if (!de || !de->enabled) { - dev_err(ti->ph->dev, - "MSG - Received INVALID DE - ID:%u enabled:%c\n", - de_id, de ? (de->enabled ? 'Y' : 'N') : 'X'); + trace_scmi_tlm_access(de_id, de ? "MSG_DE_DISABLED" : + "MSG_DE_UNKNOWN", 0, 0); continue; } =20 tde =3D to_tde(de); - guard(mutex)(&tde->mtx); - tde->cached_msg =3D true; - tde->last_val =3D LINE_DATA_GET_RAW(&payld->tsl); - /* TODO BLK_TS in notification payloads */ - tde->last_ts =3D HAS_LINE_EXT_RAW(meta) && - LINE_TS_VALID_RAW(meta) ? - LINE_TSTAMP_GET_RAW(&payld->tsl) : 0; + scoped_guard(mutex, &tde->mtx) { + tde->cached_msg =3D cacheable; + sample.val =3D LINE_DATA_GET_RAW(&payld->tsl); + sample.tstamp =3D HAS_LINE_EXT_RAW(meta) && + LINE_TS_VALID_RAW(meta) ? + LINE_TSTAMP_GET_RAW(&payld->tsl) : 0; + + /* Update the cache holding the TDE mutex */ + __scmi_telemetry_tde_cache_update(tde, &sample, NULL); + } + scmi_telemetry_uuid_link(tde, ti->primary_uuid); =20 - trace_scmi_tlm_collect(tde->last_ts, tde->de.info->id, - tde->last_val, "MESSAGE"); + trace_scmi_tlm_collect(sample.tstamp, de->info->id, sample.val, + "MESSAGE"); } } =20 @@ -3230,6 +3245,126 @@ static const struct scmi_telemetry_proto_ops tlm_pr= oto_ops =3D { .reset =3D scmi_telemetry_reset, }; =20 +static bool +scmi_telemetry_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + struct telemetry_info *ti =3D 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 =3D payld; + struct scmi_telemetry_update_report *r =3D report; + size_t dwords_sz, msg_payld_sz; + + /* At least sized as an empty notification */ + if (payld_sz < sizeof(*p)) + return NULL; + + r->timestamp =3D timestamp; + r->agent_id =3D le32_to_cpu(p->agent_id); + r->status =3D le32_to_cpu(p->status); + r->num_dwords =3D le32_to_cpu(p->num_dwords); + + /*Check boundaries and wraparounds */ + if (check_mul_overflow(r->num_dwords, sizeof(r->dwords[0]), &dwords_sz)) + return NULL; + + if (check_add_overflow(sizeof(*p), dwords_sz, &msg_payld_sz)) + return NULL; + + if (msg_payld_sz > payld_sz) + return NULL; + + /* + * 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_from_le32(r->dwords, p->array, r->num_dwords); + + *src_id =3D 0; + + return r; +} + +static const struct scmi_event tlm_events[] =3D { + { + .id =3D SCMI_EVENT_TELEMETRY_UPDATE, + .max_payld_sz =3D 0, + .max_report_sz =3D 0, + }, +}; + +static const struct scmi_event_ops tlm_event_ops =3D { + .is_notify_supported =3D scmi_telemetry_notify_supported, + .set_notify_enabled =3D scmi_telemetry_set_notify_enabled, + .fill_custom_report =3D scmi_telemetry_fill_custom_report, +}; + +static const struct scmi_protocol_events tlm_protocol_events =3D { + .queue_sz =3D SCMI_PROTO_QUEUE_SZ, + .ops =3D &tlm_event_ops, + .evts =3D tlm_events, + .num_events =3D ARRAY_SIZE(tlm_events), + .num_sources =3D 1, +}; + +static int scmi_telemetry_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct scmi_telemetry_update_report *er =3D data; + struct telemetry_info *ti =3D telemetry_nb_to_info(nb); + + if (er->status) { + trace_scmi_tlm_access(0, "BAD_NOTIF_MSG", 0, 0); + return NOTIFY_DONE; + } + + trace_scmi_tlm_access(0, "TLM_UPDATE_MSG", 0, 0); + /* Lookup the embedded DEs in the notification payload ... */ + if (er->num_dwords) { + size_t payld_bytes =3D sizeof(er->dwords[0]) * er->num_dwords; + + __le32 *dwords __free(kvfree) =3D + kvcalloc(er->num_dwords, sizeof(*dwords), GFP_KERNEL); + if (!dwords) + return NOTIFY_DONE; + + /* The report payload size was bound checked on rx */ + memcpy_to_le32(dwords, er->dwords, er->num_dwords); + /* + * Note that the DEs received via Notifications can only be + * updated that way, and as such are cached at each reception + * and never read on-demand. + */ + scmi_telemetry_msg_payld_process(ti, er->num_dwords, dwords, + payld_bytes, true); + } + + /* ...scan the SHMTI/FCs for any other DE updates. */ + if (ti->streaming_mode) + scmi_telemetry_scan_update(ti); + + return NOTIFY_OK; +} + /** * scmi_telemetry_resources_alloc - Resources allocation * @ti: A reference to the telemetry info descriptor for this instance @@ -3591,7 +3726,27 @@ static int scmi_telemetry_protocol_init(const struct= scmi_protocol_handle *ph) =20 ti->info.base.version =3D ph->version; =20 - return ph->set_priv(ph, ti); + ret =3D ph->set_priv(ph, ti); + 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 =3D &scmi_telemetry_notifier; + ret =3D ph->instance_notifier_register(ph, SCMI_EVENT_TELEMETRY_UPDATE, + NULL, &ti->telemetry_nb); + if (ret) { + dev_err(ph->dev, + "Could NOT register Telemetry notifier\n"); + return ret; + } + } + + return 0; } =20 static const struct scmi_protocol scmi_telemetry =3D { @@ -3599,6 +3754,7 @@ static const struct scmi_protocol scmi_telemetry =3D { .owner =3D THIS_MODULE, .instance_init =3D &scmi_telemetry_protocol_init, .ops =3D &tlm_proto_ops, + .events =3D &tlm_protocol_events, .supported_version =3D SCMI_PROTOCOL_SUPPORTED_VERSION, }; =20 diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 5a2787b30120..3d7b7dde6b64 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1252,6 +1252,7 @@ enum scmi_notification_events { SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER =3D 0x0, SCMI_EVENT_POWERCAP_CAP_CHANGED =3D 0x0, SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED =3D 0x1, + SCMI_EVENT_TELEMETRY_UPDATE =3D 0x0, }; =20 struct scmi_power_state_changed_report { @@ -1339,4 +1340,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 */ --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F1EEC1FDA61; Sun, 20 Sep 2026 09:21:02 +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=1789896069; cv=none; b=IN5eOaS5/iLMd6YBdZZtk14bOBa4zYbrFmx3SD3cMXSghYFFVMLbwNVL2ovz7/gac5sRXsFwkrtErH5hOufQs5+MkRdhCWila9q720sJfoND5rFnqurpEhqmOTGAUvqLyvv9Crd5AwULCqsdG2+R7dOrYufYRglXqhyTxRx96Fs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896069; c=relaxed/simple; bh=ssKLc0gmHRriQ3weqfKRAUuBtvfKxogevoBM4v95/B0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MqxHQJKOWobc8lnSMC1tsSxfZlWhVuSbuLOWDH7KGqGtRI+Qrz0gx/qqS2ehi7VDYwipSXxA02L0bktJ1JVI26lNdEjjC+oKk1UTwBwCCTFV/UCmHMIRsI6KimZN0x7j+0jvvO5BqhX2Kgo9mzc9PSEVEgH3jJfhbenpCV249Ds= 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=pOEMfHBA; 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="pOEMfHBA" 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 2C4A91D6F; Sun, 20 Sep 2026 02:20:58 -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 BC30B3F59E; Sun, 20 Sep 2026 02:20:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896061; bh=ssKLc0gmHRriQ3weqfKRAUuBtvfKxogevoBM4v95/B0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pOEMfHBAZKS2aJMyB0B6Sw888eXjs7Ycy/QA1v+1sh1O6qWKxMWjCMlVU/Oa+0MoN CTqAKSvYD8uWcztjeWy3YoXBjCWPKfYycfQlYt9k4cWADSJQOCskjfVuQHDjCaQTbd LWDdYymu+gZTeliJRO9jo9/5vGSpfLWvssLM1ok4= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 12/25] firmware: arm_scmi: Add support for boot-on Telemetry Date: Sun, 20 Sep 2026 10:19:15 +0100 Message-ID: <20260920091928.2014972-13-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 the initialization and discovery logic needed to detect when the platform SCMI server is configured with telemetry enabled at boot and perform all the needed resource enumerations to keep the kernel telemetry subsystem state aligned with the platform boot-on configurations. Signed-off-by: Cristian Marussi --- v11 --> v12 - fixed TS_RATE determination at boot-on v10 --> v11 - fix non-conformancy on CONFIG_GET when groups NON configurable - use SCAN_UPDATE also on BOOT-ON initial scan (NO discovery) - avoid 'naked' ti->rinfo accesses v8 -- > v9 - remove useless initial state lookup after RESET - properly set de->tstamp_support on DE_ENABLED_LIST v5 --> v6 - reporte properly an error on scmi_telemetry_config_lookup() failure (Sashiko) v4 --> v5 - use renamed grp_id instead of old id in group descriptors - fixes sparse warnings v3 --> v4 - bail-out FW_BUG errors - fix typos in comments - track boot-on Telemetry DE states v2 --> v3 - split from monolithic telemetry protocol patch - swap logic in scmi_telemetry_initial_state_lookup --- drivers/firmware/arm_scmi/telemetry.c | 239 +++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index dde643f5de4d..ae712e95daa0 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -831,8 +831,13 @@ static int scmi_telemetry_de_descriptor_parse(struct t= elemetry_info *ti, /* Variably sized depending on TS support */ *next +=3D sizeof(*line_ts_rate); } else if (tde->ts_type =3D=3D TSTAMP_BLK) { - /* Setup default BLK TS value at first */ - tde->de.info->ts_rate =3D ti->default_blk_ts_rate; + /* + * Setup default BLK TS rate value at first, if it has NOT + * already been picked from the BLK_TS line by the boot-on + * SCAN_UPDATE. + */ + if (!tde->de.info->ts_rate) + tde->de.info->ts_rate =3D ti->default_blk_ts_rate; } =20 if (tde->de.fc_support) { @@ -941,6 +946,214 @@ static int iter_de_descr_process_response(const struc= t scmi_protocol_handle *ph, return ret; } =20 +static int scmi_telemetry_config_lookup(struct telemetry_info *ti, + unsigned int grp_id, bool *enabled, + unsigned int *active_update_interval) +{ + const struct scmi_protocol_handle *ph =3D ti->ph; + struct scmi_msg_telemetry_config_get *msg; + struct scmi_msg_resp_telemetry_config_get *resp; + struct scmi_xfer *t; + u32 flags; + int ret; + + ret =3D ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_GET, + sizeof(*msg), sizeof(*resp), &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->grp_id =3D cpu_to_le32(grp_id); + + /* Spec mandates the ALL selector when per-group config not available */ + if (!ti->info.per_group_config_support) + flags =3D TELEMETRY_GET_SELECTOR_ALL; + else + flags =3D grp_id =3D=3D SCMI_TLM_GRP_INVALID ? + TELEMETRY_GET_SELECTOR_ORPHANS : + TELEMETRY_GET_SELECTOR_GROUP; + + msg->flags =3D cpu_to_le32(flags); + + resp =3D t->rx.buf; + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) { + *enabled =3D le32_get_bits(resp->control, TELEMETRY_ENABLE); + *active_update_interval =3D + SCMI_TLM_GET_UPDATE_INTERVAL(le32_to_cpu(resp->sampling_rate)); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_telemetry_group_config_lookup(struct telemetry_info *ti, + struct scmi_telemetry_group *grp) +{ + return scmi_telemetry_config_lookup(ti, grp->info->grp_id, &grp->enabled, + &grp->active_update_interval); +} + +static void iter_enabled_list_prepare_message(void *message, + unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_telemetry_de_enabled_list *msg =3D message; + + msg->index =3D cpu_to_le32(desc_index); + msg->flags =3D 0; +} + +static int iter_enabled_list_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_msg_resp_telemetry_de_enabled_list *r =3D response; + + st->num_returned =3D le32_get_bits(r->flags, GENMASK(15, 0)); + st->num_remaining =3D le32_get_bits(r->flags, GENMASK(31, 16)); + + if (st->rx_len < (sizeof(*r) + sizeof(r->entry[0]) * st->num_returned)) + return -EINVAL; + + /* + * total enabled is not declared previously anywhere so we + * assume it's returned+remaining on first call. + */ + if (!st->max_resources) + st->max_resources =3D st->num_returned + st->num_remaining; + + return 0; +} + +static int +iter_enabled_list_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + const struct scmi_msg_resp_telemetry_de_enabled_list *r =3D response; + const struct scmi_enabled_de_desc *desc; + struct telemetry_info *ti =3D priv; + struct telemetry_de *tde; + u32 de_id; + int ret; + + desc =3D &r->entry[st->loop_idx]; + de_id =3D le32_to_cpu(desc->id); + if (scmi_telemetry_tde_lookup(ti, de_id)) { + dev_err(ph->dev, + "Found INVALID DE with DUPLICATED ID:0x%08X\n", de_id); + return -EINVAL; + } + + tde =3D scmi_telemetry_tde_get(ti, de_id); + if (IS_ERR(tde)) + return PTR_ERR(tde); + + tde->de.info->id =3D de_id; + tde->de.enabled =3D true; + tde->de.tstamp_enabled =3D le32_to_cpu(desc->mode) =3D=3D DE_ENABLED_WITH= _TSTAMP; + /* + * Guessing that, if a DE is reported as enabled with timestamp, it has + * to support timestamp: NOTE THAT the timestamp type (tde->ts_type) can + * ONLY be discovered later by DE_DESCRIPTION. + */ + tde->de.tstamp_support =3D tde->de.tstamp_enabled; + + ret =3D scmi_telemetry_tde_register(ti, tde); + if (ret) { + scmi_telemetry_free_tde_put(ti, tde); + return ret; + } + + scmi_telemetry_de_state_update(ti, ENA_STATE, NULL, true); + if (tde->de.tstamp_enabled) + scmi_telemetry_de_state_update(ti, ENA_TSTAMP, NULL, true); + + dev_dbg(ph->dev, "Registered new ENABLED DE with ID:0x%08X\n", + tde->de.info->id); + + return 0; +} + +static int scmi_telemetry_enumerate_des_enabled_list(struct telemetry_info= *ti) +{ + const struct scmi_protocol_handle *ph =3D ti->ph; + struct scmi_telemetry_res_info *rinfo; + struct scmi_iterator_ops ops =3D { + .prepare_message =3D iter_enabled_list_prepare_message, + .update_state =3D iter_enabled_list_update_state, + .process_response =3D iter_enabled_list_process_response, + }; + void *iter; + int ret; + + iter =3D ph->hops->iter_response_init(ph, &ops, 0, + TELEMETRY_DE_ENABLED_LIST, + sizeof(u32) * 2, ti); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret =3D ph->hops->iter_response_run(iter); + if (ret) + return ret; + + /* Get rinfo as it is...without triggering an enumeration */ + rinfo =3D __scmi_telemetry_resources_get(ti); + + dev_info(ti->ph->dev, "Found %u enabled DEs.\n", rinfo->num_des); + + return 0; +} + +static int scmi_telemetry_initial_state_lookup(struct telemetry_info *ti) +{ + struct device *dev =3D ti->ph->dev; + int ret; + + ret =3D scmi_telemetry_config_lookup(ti, SCMI_TLM_GRP_INVALID, + &ti->info.enabled, + &ti->info.active_update_interval); + if (ret) + return ret; + + if (!ti->info.enabled) + return 0; + + /* + * When Telemetry is found already enabled on the platform, proceed with + * passive discovery using DE_ENABLED_LIST and TDCF scanning: note that + * this CAN only discover DEs exposed via SHMTIs. + * FastChannel DEs need a proper DE_DESCRIPTION enumeration, while, even + * though incoming Notifications could be used for passive discovery too, + * it would carry a considerable risk of assimilating trash as DEs. + */ + dev_info(dev, + "Telemetry found enabled with update interval %lux10^%d\n", + SCMI_TLM_GET_UPDATE_INTERVAL_SECS(ti->info.active_update_interval), + SCMI_TLM_GET_UPDATE_INTERVAL_EXP(ti->info.active_update_interval)); + /* + * Query enabled DEs list: collect states. It will include DEs from any + * interface. Enabled groups still NOT enumerated. + */ + ret =3D scmi_telemetry_enumerate_des_enabled_list(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot query enabled DE list. Abort.\n"); + return ret; + } + + /* Discover DEs on SHMTis: collect states/offsets/values */ + for (int id =3D 0; id < ti->num_shmti; id++) { + ret =3D scmi_telemetry_shmti_scan(ti, id, SCAN_UPDATE); + if (ret) + dev_warn(dev, + "Failed discovery-scan of SHMTI ID:%d - ret:%d\n", + id, ret); + } + + return 0; +} + static int scmi_telemetry_de_groups_init(struct device *dev, struct telemetry_info *t= i) { @@ -1023,6 +1236,9 @@ scmi_telemetry_de_groups_init(struct device *dev, str= uct telemetry_info *ti) } } =20 + for (int i =3D 0; i < ti->info.base.num_groups; i++) + scmi_telemetry_group_config_lookup(ti, &rinfo->grps[i]); + /* Expose all groups once all fully initialized */ rinfo->num_groups =3D num_groups; =20 @@ -1988,9 +2204,17 @@ static void scmi_telemetry_tdcf_data_parse(struct te= lemetry_info *ti, tde->eplg =3D SHMTI_EPLG(shmti); tde->offset =3D (void __iomem *)payld - (void __iomem *)shmti->base; =20 + /* Just in case it was passively discovered boot-on */ + if (tde->ts_type =3D=3D TSTAMP_NONE) { + if (use_blk_ts) + tde->ts_type =3D TSTAMP_BLK; + else if (LINE_TS_VALID(payld)) + tde->ts_type =3D TSTAMP_LINE; + } + dev_dbg(ti->ph->dev, - "TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u\n", - tde->de.info->id, tde->base, tde->offset); + "TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u ts_type:%d\n", + tde->de.info->id, tde->base, tde->offset, tde->ts_type); } =20 /* Has any value/tstamp really changed ?*/ @@ -3225,6 +3449,7 @@ static int scmi_telemetry_reset(const struct scmi_pro= tocol_handle *ph) struct telemetry_info *ti =3D ph->get_priv(ph); =20 scmi_telemetry_local_resources_reset(ti); + /* NO need to lookup the initial state we have just reset all */ } =20 ph->xops->xfer_put(ph, t); @@ -3724,6 +3949,12 @@ static int scmi_telemetry_protocol_init(const struct= scmi_protocol_handle *ph) return ret; } =20 + ret =3D scmi_telemetry_initial_state_lookup(ti); + if (ret) { + dev_err(dev, FW_BUG "Cannot retrieve initial state. Abort.\n"); + return ret; + } + ti->info.base.version =3D ph->version; =20 ret =3D ph->set_priv(ph, ti); --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8D83B3E00A3; Sun, 20 Sep 2026 09:21:08 +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=1789896072; cv=none; b=f/i0PCGtR0Gz4odGvhxhiKZ0xusZUFeXIDnVmZgGSVdZS5YLN5lXXVYZPo6duXJjQC0OrcxtkgZeV4crohDTdlKUrrJA4JULl1QhS0T5S9TtVDGBpCXwBd3EyAKzyZdnRzVPb4WeUDCb7HY7mdQEviLTupnaxIvw7SYEmjAFj3A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896072; c=relaxed/simple; bh=VBV18BwcjkHxRBmfusIg+mEAGZIeNHamTOoODUrr6+g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jrWFVWWr++stlP3zr1SyM6WRKJ388oGdlzxJqU8pb3ptmwoiRApt1xrGf9hYTLInnHlG5k7eG8/SZzNZS3v6PLBSC7TcI4QTmImLZSjBRp5RkU3HdoUhzAMT/czcw2CY9Qt/TR+xVxeivhCReLB1vXGRlVFi//zBA1vAoSKAjE4= 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=p2i1sq7t; 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="p2i1sq7t" 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 747531691; Sun, 20 Sep 2026 02:21:02 -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 0FD653F59E; Sun, 20 Sep 2026 02:21:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896066; bh=VBV18BwcjkHxRBmfusIg+mEAGZIeNHamTOoODUrr6+g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p2i1sq7tZslcKAGJ/zGiaP4/aunYBEssoH1ueH3oHJBH8Ko32RW2Oo274/5i8PTvu DxazWS/ajjaD2IY9CTIw+VnCZ8V1J7K7RdakBFNuo2v98hCPcF29FbI2KwVkpOEdBm G/F7GHqLTNX71Tk3qKJ9rO6yGjSJ+x2PrGj0xkOc= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 13/25] firmware: arm-scmi: Add telemetry generic event support Date: Sun, 20 Sep 2026 10:19:16 +0100 Message-ID: <20260920091928.2014972-14-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 to allow SCMI driver users to subscribe/unsubscribe to specific Telemetry abstract (non protocol) events. This enables SCMI drivers to subscribe to available events by registering an eventfd context that the core SCMI stack will use as a signalling mechanisms. Signed-off-by: Cristian Marussi --- v11 --> v12 - dropped eventfd_ctx_put on external ref v10 --> v11 - move events cleanup into resource_free v8 --> v9 - add a TLM_EVT_DUMMY placeholder (waiting for more events later on..) --- drivers/firmware/arm_scmi/telemetry.c | 96 +++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 16 +++++ 2 files changed, 112 insertions(+) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index ae712e95daa0..c76409083160 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -483,6 +484,11 @@ enum de_state { ENA_MAX }; =20 +struct telemetry_event { + struct eventfd_ctx *ctx; + struct list_head item; +}; + struct telemetry_info { bool streaming_mode; unsigned int num_shmti; @@ -511,6 +517,9 @@ struct telemetry_info { /* Protect global and per-group configuration data */ struct mutex config_mtx; struct scmi_telemetry_info info; + /* Mutex to protect access to @events */ + struct mutex events_mtx; + struct list_head events[SCMI_TLM_EVT_MAX]; struct notifier_block telemetry_nb; atomic_t rinfo_initializing; struct completion rinfo_initdone; @@ -538,6 +547,21 @@ struct mutex *lock_for_payld(struct mutex *locks, stru= ct payload __iomem *payld) return &locks[hash_long((__force unsigned long)payld, PAYLD_HASH_BITS)]; } =20 +static int scmi_telemetry_event_signal(struct telemetry_info *ti, + enum scmi_telemetry_event type) +{ + struct telemetry_event *evt; + + if (type >=3D SCMI_TLM_EVT_MAX) + return -EINVAL; + + guard(mutex)(&ti->events_mtx); + list_for_each_entry(evt, &ti->events[type], item) + eventfd_signal(evt->ctx); + + return 0; +} + static inline void scmi_telemetry_de_state_update(struct telemetry_info *ti, enum de_state st= ate, bool *current_state, const bool next_state) @@ -3457,6 +3481,54 @@ static int scmi_telemetry_reset(const struct scmi_pr= otocol_handle *ph) return ret; } =20 +static int scmi_telemetry_event_subscribe(const struct scmi_protocol_handl= e *ph, + enum scmi_telemetry_event type, + struct eventfd_ctx *ctx) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + struct telemetry_event *evt; + + if (type >=3D SCMI_TLM_EVT_MAX) + return -EINVAL; + + evt =3D kzalloc_obj(*evt); + if (!evt) + return -ENOMEM; + + evt->ctx =3D ctx; + guard(mutex)(&ti->events_mtx); + list_add(&evt->item, &ti->events[type]); + + trace_scmi_tlm_access(0, "TLM_EVT_SUBS", 0, 0); + + return 0; +} + +static int scmi_telemetry_event_unsubscribe(const struct scmi_protocol_han= dle *ph, + enum scmi_telemetry_event type, + struct eventfd_ctx *ctx) +{ + struct telemetry_info *ti =3D ph->get_priv(ph); + struct telemetry_event *evt, *n; + + if (type >=3D SCMI_TLM_EVT_MAX) + return -EINVAL; + + guard(mutex)(&ti->events_mtx); + list_for_each_entry_safe(evt, n, &ti->events[type], item) { + if (evt->ctx =3D=3D ctx) { + list_del(&evt->item); + kfree(evt); + + trace_scmi_tlm_access(0, "TLM_EVT_UNSUBS", 0, 0); + + break; + } + } + + return 0; +} + 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, @@ -3468,6 +3540,8 @@ static const struct scmi_telemetry_proto_ops tlm_prot= o_ops =3D { .des_bulk_read =3D scmi_telemetry_des_bulk_read, .des_sample_get =3D scmi_telemetry_des_sample_get, .reset =3D scmi_telemetry_reset, + .event_subscribe =3D scmi_telemetry_event_subscribe, + .event_unsubscribe =3D scmi_telemetry_event_unsubscribe, }; =20 static bool @@ -3687,6 +3761,22 @@ __scmi_telemetry_resources_get(struct telemetry_info= *ti) return smp_load_acquire(&ti->rinfo); } =20 +static void scmi_telemetry_event_cleanup(struct telemetry_info *ti) +{ + guard(mutex)(&ti->events_mtx); + /* Clear any residual events */ + for (int type =3D 0; type < SCMI_TLM_EVT_MAX; type++) { + struct telemetry_event *evt, *n; + + list_for_each_entry_safe(evt, n, &ti->events[type], item) { + dev_warn(ti->ph->dev, + "Found UN-SUBSCRIBED event type %d!\n", type); + list_del(&evt->item); + kfree(evt); + } + } +} + static void scmi_telemetry_resources_free(void *arg) { struct scmi_telemetry_res_info *rinfo; @@ -3733,6 +3823,9 @@ static void scmi_telemetry_resources_free(void *arg) scmi_telemetry_line_put(line); xa_destroy(&ti->xa_lines); =20 + /* Cleanup any still registered event */ + scmi_telemetry_event_cleanup(ti); + kfree(ti->tdes); kfree(rinfo->des); kfree(rinfo->dei_store); @@ -3898,6 +3991,9 @@ static int scmi_telemetry_instance_init(struct teleme= try_info *ti) if (ret) return ret; =20 + for (int i =3D 0; i < SCMI_TLM_EVT_MAX; i++) + INIT_LIST_HEAD(&ti->events[i]); + mutex_init(&ti->events_mtx); atomic_set(&ti->des_enabled[ENA_STATE], 0); atomic_set(&ti->des_enabled[ENA_TSTAMP], 0); mutex_init(&ti->des_enabled_mtx); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 3d7b7dde6b64..b4b3e95547ba 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -994,6 +995,11 @@ struct scmi_telemetry_de_sample { unsigned long long val; }; =20 +enum scmi_telemetry_event { + SCMI_TLM_EVT_DUMMY, + SCMI_TLM_EVT_MAX, +}; + /** * struct scmi_telemetry_proto_ops - represents the various operations pro= vided * by SCMI Telemetry Protocol @@ -1017,6 +1023,10 @@ struct scmi_telemetry_de_sample { * This causes an immediate update platform-side of all the * enabled DEs. * @reset: reset configuration and telemetry data. + * @event_subscribe: subscribe to the specified event @type using the prov= ided + * @eventfd_ctx. + * @event_unsubscribe: unsubscribe to the specified event @type the previo= usly + * registered @eventfd_ctx. */ struct scmi_telemetry_proto_ops { const struct scmi_telemetry_info __must_check *(*info_get) @@ -1044,6 +1054,12 @@ struct scmi_telemetry_proto_ops { 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 (*event_subscribe)(const struct scmi_protocol_handle *ph, + enum scmi_telemetry_event type, + struct eventfd_ctx *ctx); + int (*event_unsubscribe)(const struct scmi_protocol_handle *ph, + enum scmi_telemetry_event type, + struct eventfd_ctx *ctx); int (*reset)(const struct scmi_protocol_handle *ph); }; =20 --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 578113E558A; Sun, 20 Sep 2026 09:21:10 +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=1789896076; cv=none; b=IVdIUy2RJKHt/UDqTjT6DRDK/oOvpUPcuZRfUS8xlnSsaa6T83QPDod+xZeXJqZgqTx4iDIsr36rxV605qNLWloZFuHIE6Hi8Qv41290CAAQTUZMVEQp9ThebYdlmC7VVRHMFMxB1F/r7iAvaA3crJpzN8ot4NSus6UvL1dULmk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896076; c=relaxed/simple; bh=t9wyyeFMtxTXqDtnieZJaEnVceYHrygP7s5U8LrdN4U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cH/KzKDFfwI55ADm5chpYhTlmzC3A///clUZgXEznOBvCPcGPfgIjmEUMfHeq54eed/hdCsSHDWR8fFOPyn0UWudg9+N2kJw9SXL52TIETjMcK50KqzArXusFWptnh3d348YsgRVBgZFYkfjro4BSV7gu0bP9ktvpzVrNSGNY8A= 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=LAfe+EYK; 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="LAfe+EYK" 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 C20B71650; Sun, 20 Sep 2026 02:21:06 -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 6F4F13F59E; Sun, 20 Sep 2026 02:21:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896070; bh=t9wyyeFMtxTXqDtnieZJaEnVceYHrygP7s5U8LrdN4U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LAfe+EYKuJjFImL9E5GNvvtEJzJICNNETd3BtNGHQ996sgFy8dWc/5pNwTQ4HmSU2 E6DePx9z6IdxoYs2IjUl5thX3i5XIKGbvrwG7xqc+r+PtYZlRBZ4kT082CwW4uhfnV +LSMaNSWiRGW24wvu23rzgCXgSJTd3iQs+oKN+jM= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 14/25] firmware: arm_scmi: Add Telemetry generation counter event Date: Sun, 20 Sep 2026 10:19:17 +0100 Message-ID: <20260920091928.2014972-15-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 a per-instance generation counter to track configuration changes which uses the common telemetry events framework. Signed-off-by: Cristian Marussi --- v11 -->v12 - reviewed generation counter update logic v6 --> v7 - use new Generic event mechanism based on EventFD instead of WaitQ v5 --> v6 - add relese semantic to the atomic set of the generation counter - use new eventfd_ctx common event logic --- drivers/firmware/arm_scmi/telemetry.c | 48 ++++++++++++++++++++++++++- include/linux/scmi_protocol.h | 6 ++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index c76409083160..ada4838d2351 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include =20 #include "protocols.h" #include "notify.h" @@ -35,6 +37,7 @@ /* Updated only after ALL the mandatory features for that version are merg= ed */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 =20 +#define SCMI_TLM_GENERATION_ONE (SCMI_TLM_GENERATION_INVALID + 1U) #define SCMI_TLM_TDCF_MAX_RETRIES 5 =20 #define SCMI_TLM_DE_IMPL_NUM_DWORDS 4 @@ -719,6 +722,37 @@ scmi_telemetry_tde_cache_lookup(struct telemetry_de *t= de, return 0; } =20 +static inline void __scmi_telemetry_generation_set(struct telemetry_info *= ti, + unsigned int new) +{ + int ret; + + atomic_set_release(&ti->info.generation, new); + + ret =3D scmi_telemetry_event_signal(ti, SCMI_TLM_EVT_GENERATION); + if (ret) + dev_warn_once(ti->ph->dev, + "Could NOT signal telemetry event %d\n", + SCMI_TLM_EVT_GENERATION); +} + +static inline void scmi_telemetry_generation_update(struct telemetry_info = *ti) +{ + unsigned int next; + + lockdep_assert_held(&ti->config_mtx); + + /* Wrap around skipping invalid generation 0 */ + next =3D (atomic_read(&ti->info.generation) + 1) ?: SCMI_TLM_GENERATION_O= NE; + + __scmi_telemetry_generation_set(ti, next); +} + +static inline void scmi_telemetry_generation_reset(struct telemetry_info *= ti) +{ + __scmi_telemetry_generation_set(ti, SCMI_TLM_GENERATION_ONE); +} + struct scmi_tlm_de_priv { struct telemetry_info *ti; void *next; @@ -2734,6 +2768,8 @@ static int __scmi_telemetry_state_set(const struct sc= mi_protocol_handle *ph, tstamp_enabled_state, *tstamp); =20 + /* A local change can have an impact anyway */ + scmi_telemetry_generation_update(ti); return 0; } =20 @@ -2766,11 +2802,13 @@ static int __scmi_telemetry_state_set(const struct = scmi_protocol_handle *ph, ret =3D ph->xops->do_xfer(ph, t); if (!ret) { ret =3D scmi_telemetry_state_set_resp_process(ti, obj, resp, is_group); - if (!ret) + if (!ret) { /* Update cached state on success */ scmi_telemetry_state_cache_update(ti, enable, enabled_state, tstamp, tstamp_enabled_state, grp, de); + scmi_telemetry_generation_update(ti); + } } =20 ph->xops->xfer_put(ph, t); @@ -2959,6 +2997,9 @@ scmi_telemetry_collection_configure(const struct scmi= _protocol_handle *ph, =20 ph->xops->xfer_put(ph, t); =20 + if (!ret) + scmi_telemetry_generation_update(ti); + return ret; } =20 @@ -3473,6 +3514,9 @@ static int scmi_telemetry_reset(const struct scmi_pro= tocol_handle *ph) struct telemetry_info *ti =3D ph->get_priv(ph); =20 scmi_telemetry_local_resources_reset(ti); + /* Reset generation now that server has been reset */ + scmi_telemetry_generation_reset(ti); + /* NO need to lookup the initial state we have just reset all */ } =20 @@ -3994,6 +4038,8 @@ static int scmi_telemetry_instance_init(struct teleme= try_info *ti) for (int i =3D 0; i < SCMI_TLM_EVT_MAX; i++) INIT_LIST_HEAD(&ti->events[i]); mutex_init(&ti->events_mtx); + /* Generation counter init */ + atomic_set(&ti->info.generation, SCMI_TLM_GENERATION_ONE); atomic_set(&ti->des_enabled[ENA_STATE], 0); atomic_set(&ti->des_enabled[ENA_TSTAMP], 0); mutex_init(&ti->des_enabled_mtx); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index b4b3e95547ba..56b098bc472d 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -891,6 +891,7 @@ enum scmi_telemetry_collection { SCMI_TLM_SINGLE_READ, }; =20 +#define SCMI_TLM_GENERATION_INVALID 0U #define SCMI_TLM_GRP_INVALID 0xFFFFFFFF =20 struct scmi_telemetry_intervals { @@ -986,6 +987,7 @@ struct scmi_telemetry_info { bool enabled; bool notif_enabled; enum scmi_telemetry_collection current_mode; + atomic_t generation; }; =20 struct scmi_telemetry_de_sample { @@ -996,7 +998,7 @@ struct scmi_telemetry_de_sample { }; =20 enum scmi_telemetry_event { - SCMI_TLM_EVT_DUMMY, + SCMI_TLM_EVT_GENERATION, SCMI_TLM_EVT_MAX, }; =20 @@ -1022,11 +1024,11 @@ enum scmi_telemetry_event { * the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. - * @reset: reset configuration and telemetry data. * @event_subscribe: subscribe to the specified event @type using the prov= ided * @eventfd_ctx. * @event_unsubscribe: unsubscribe to the specified event @type the previo= usly * registered @eventfd_ctx. + * @reset: reset configuration and telemetry data. */ struct scmi_telemetry_proto_ops { const struct scmi_telemetry_info __must_check *(*info_get) --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 569683EB0E9; Sun, 20 Sep 2026 09:21:16 +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=1789896080; cv=none; b=k6dqcLnpyzujaNSSJ9NsMdW5YaLoxdNARiEXcwnBoOSs7VYVbRETOfasS+4Pp8VQR70dm4a8E5pbeLKQUcb/SfbuDd5CY5ntrSiKIzTIm7MJ3gahTxWsCRAPJF2OgKOy1IqUq6qKG17rOhazf6ezxJzR4Ydef0vO/s5Ih9MYlJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896080; c=relaxed/simple; bh=lcqh6AHoDp987sH+I8s0r3HLuAZo2gsUA6yAYeHiGJU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vbege7Trbdsl0Zxq3FoByiW4qj/tA28dtCuzbpR+du7oW3zGqZuLl1MvIUbRq3XHyXvKTIN0KaUanKAemX8LcH6fLfqbPdQ4QyjHR+s/7mRmEi89f7eRYQUd8Vbmk7y/y/ltwVoNgkVFn880t5lvgk3uMi+m66wXNNV276Agdhc= 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=t1ftMKiq; 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="t1ftMKiq" 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 E944E1691; Sun, 20 Sep 2026 02:21:10 -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 A929C3F59E; Sun, 20 Sep 2026 02:21:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896074; bh=lcqh6AHoDp987sH+I8s0r3HLuAZo2gsUA6yAYeHiGJU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t1ftMKiqaKLMONF1M6V3Es3c1gzk/fvdQC+5/LN8ZRo0e3JqSWQIEV6Kz8tgKGmPc r6FNfgeax9Jq58Db+k+DQqzlEbcnjCSje/t8heCxySyp4A0OELc9aVBE44Id3APGcH TSRUNrUyV7sJN2LGM5iuAliXYLlUSLEv2PBk+etk= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 15/25] firmware: arm_scmi: Add common per-protocol debugfs support Date: Sun, 20 Sep 2026 10:19:18 +0100 Message-ID: <20260920091928.2014972-16-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" Allow interested SCMI protocols to register their own specific debugfs entries under a common per-instance and per-protocol subtree rooted at /sys/kernel/debug/scmi//protocols// Expose a helper to enable protocol initialization code to get access to such per-protocol/per-instance dentries in order to be able to install their own dedicated debugfs entries. Per-protocol debugfs support is configurable and default off. Signed-off-by: Cristian Marussi --- v8 --> v9 - avoid NULL deref on DBG cleanup path v5 --> v6 - bail out of dentry_get helper when SCMI debug susbsytem failed to init - remove stale SCMI protocol operations - add a preoper clean up of debugfs when protocol init fails --- drivers/firmware/arm_scmi/Kconfig | 14 +++++++++++ drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/driver.c | 35 +++++++++++++++++++++++++-- drivers/firmware/arm_scmi/protocols.h | 6 +++++ include/linux/scmi_protocol.h | 1 - 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/= Kconfig index 783c24a20e29..f506c4d1d96a 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -69,6 +69,20 @@ config ARM_SCMI_DEBUG_COUNTERS such useful debug counters. This can be helpful for debugging and SCMI monitoring. =20 +config ARM_SCMI_DEBUG_PROTOCOLS + bool "Enable SCMI protocols debug" + select ARM_SCMI_NEED_DEBUGFS + depends on DEBUG_FS + default n + help + Enables per-protocol specific debug features, where available. + When provided, such per-protocol debugfs entries are grouped + inside a common a subtree named by the protocol number and rooted + under a per-instance 'protocols' directory. + + Such per-protocol entries subtree structure is freely defined + within the related protocol code. + config ARM_SCMI_QUIRKS bool "Enable SCMI Quirks framework" depends on JUMP_LABEL || COMPILE_TEST diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi= /common.h index 1d9e6af09672..8c0e49748c32 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -324,6 +324,7 @@ enum debug_counters { /** * struct scmi_debug_info - Debug common info * @top_dentry: A reference to the top debugfs dentry + * @protos: A reference to the top debugfs protocols subdirectory * @name: Name of this SCMI instance * @type: Type of this SCMI instance * @is_atomic: Flag to state if the transport of this instance is atomic @@ -331,6 +332,7 @@ enum debug_counters { */ struct scmi_debug_info { struct dentry *top_dentry; + struct dentry *protos; const char *name; const char *type; bool is_atomic; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi= /driver.c index 1b23d533e7e0..8bb5e4c1bbbc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -18,6 +18,7 @@ =20 #include #include +#include #include #include #include @@ -102,6 +103,8 @@ struct scmi_xfers_info { * has completed. * @ph: An embedded protocol handle that will be passed down to protocol * initialization code to identify this instance. + * @dbg: An optional reference to this protocol top debugfs directory; it = will + * be automatically recursively removed on protocol de-initialization. * * Each protocol is initialized independently once for each SCMI platform = in * which is defined by DT and implemented by the SCMI server fw. @@ -116,6 +119,7 @@ struct scmi_protocol_instance { unsigned int version; unsigned int negotiated_version; struct scmi_protocol_handle ph; + struct dentry *dbg; }; =20 #define ph_to_pi(h) container_of(h, struct scmi_protocol_instance, ph) @@ -2094,6 +2098,25 @@ static void scmi_common_fastchannel_db_ring(struct s= cmi_fc_db_info *db) SCMI_PROTO_FC_RING_DB(64); } =20 +static struct dentry * +scmi_debugfs_proto_dentry_get(const struct scmi_protocol_handle *ph) +{ + struct scmi_protocol_instance *pi =3D ph_to_pi(ph); + struct scmi_info *info =3D handle_to_scmi_info(pi->handle); + + if (!IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_PROTOCOLS) || !info->dbg) + return ERR_PTR(-ENODEV); + + if (!pi->dbg) { + char proto_dir[8]; + + snprintf(proto_dir, 8, "0x%02X", pi->proto->id); + pi->dbg =3D debugfs_create_dir(proto_dir, info->dbg->protos); + } + + return pi->dbg; +} + static const struct scmi_proto_helpers_ops helpers_ops =3D { .extended_name_get =3D scmi_common_extended_name_get, .get_max_msg_size =3D scmi_common_get_max_msg_size, @@ -2104,6 +2127,7 @@ static const struct scmi_proto_helpers_ops helpers_op= s =3D { .protocol_msg_check =3D scmi_protocol_msg_check, .fastchannel_init =3D scmi_common_fastchannel_init, .fastchannel_db_ring =3D scmi_common_fastchannel_db_ring, + .debugfs_proto_dentry_get =3D scmi_debugfs_proto_dentry_get, }; =20 /** @@ -2289,12 +2313,12 @@ scmi_alloc_init_protocol_instance(struct scmi_info = *info, /* proto->init is assured NON NULL by scmi_protocol_register */ ret =3D pi->proto->instance_init(&pi->ph); if (ret) - goto clean; + goto clean_dbg; =20 ret =3D idr_alloc(&info->protocols, pi, proto->id, proto->id + 1, GFP_KERNEL); if (ret !=3D proto->id) - goto clean; + goto clean_dbg; =20 /* * Warn but ignore events registration errors since we do not want @@ -2315,6 +2339,8 @@ scmi_alloc_init_protocol_instance(struct scmi_info *i= nfo, =20 return pi; =20 +clean_dbg: + debugfs_remove_recursive(pi->dbg); clean: /* Take care to put the protocol module's owner before releasing all */ scmi_protocol_put(proto); @@ -2430,6 +2456,8 @@ void scmi_protocol_release(const struct scmi_handle *= handle, u8 protocol_id) if (refcount_dec_and_test(&pi->users)) { void *gid =3D pi->gid; =20 + debugfs_remove_recursive(pi->dbg); + if (pi->proto->events) scmi_deregister_protocol_events(handle, protocol_id); =20 @@ -3169,6 +3197,9 @@ static struct scmi_debug_info *scmi_debugfs_common_se= tup(struct scmi_info *info) if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) scmi_debugfs_counters_setup(dbg, trans); =20 + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_PROTOCOLS)) + dbg->protos =3D debugfs_create_dir("protocols", top_dentry); + dbg->top_dentry =3D top_dentry; =20 if (devm_add_action_or_reset(info->dev, diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_s= cmi/protocols.h index 1f9925b8f495..04e196a9fbf4 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -11,6 +11,7 @@ =20 #include #include +#include #include #include #include @@ -284,6 +285,9 @@ struct scmi_fc_info { * gathering FC descriptions from the SCMI platform server. * @fastchannel_db_ring: A common helper to ring a FC doorbell. * @get_max_msg_size: A common helper to get the maximum message size. + * @debugfs_proto_dentry_get: A common helper to get a per-protocol debugf= s top + * directory to use as a root. It will be + * recursively removed on protocol de-initialization. */ struct scmi_proto_helpers_ops { int (*extended_name_get)(const struct scmi_protocol_handle *ph, @@ -307,6 +311,8 @@ struct scmi_proto_helpers_ops { u32 *rate_limit); void (*fastchannel_db_ring)(struct scmi_fc_db_info *db); int (*get_max_msg_size)(const struct scmi_protocol_handle *ph); + struct dentry *(*debugfs_proto_dentry_get) + (const struct scmi_protocol_handle *ph); }; =20 /** diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 56b098bc472d..6b1b0eee2651 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1156,7 +1156,6 @@ struct scmi_handle { void (*devm_protocol_put)(struct scmi_device *sdev, u8 proto); bool (*is_transport_atomic)(const struct scmi_handle *handle, unsigned int *atomic_threshold); - const struct scmi_notify_ops *notify_ops; }; =20 --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BF4293EC6A9; Sun, 20 Sep 2026 09:21:20 +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=1789896084; cv=none; b=t9HCp/EFVuSRv4WAsKYqLAIkoWoQapgacP16XX9m2IIkUrk298IrmTzOAGara5YqGO0eiCfQyZPzoumXrGs3OhzKrg2rF9A22SNUEM+h/ELNESY2R5Wo0FcIU+4t4QthXMeQlGFd6+0cbLr7sGE5B6XoQmVgGbDYt14AANxnn10= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896084; c=relaxed/simple; bh=lepzCoFoOSnld2fNwqgC5kvTkcktJyOziMdVACzjx3I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TVmqHYovzsJmaXOG0CKT1ka30Gt5IlpAbe2pkYmqOnr1kYX8y51fljZ9vXHI4um0YytQ0byUFo0dJtO0Qbc6cvrHvoGN8f+uVcDgryBu6opJ4DgUh3x48TxpqE1Owmk8LfJNyrbCd05U4UlR0XR/HyUu7/i4LakhsAZ6hQOfwRM= 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=gld4dXuu; 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="gld4dXuu" 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 2452E1C0A; Sun, 20 Sep 2026 02:21:15 -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 CC0F63F59E; Sun, 20 Sep 2026 02:21:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896078; bh=lepzCoFoOSnld2fNwqgC5kvTkcktJyOziMdVACzjx3I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gld4dXuuiorZRdlZprOK5GOP23RkmC/2ha3ITW1yDr/LiBczqEP88fT8f1zIy3LHC aGGUaFEa4V4DtMmoLU6ipnQKiW53440ORf1iHMEcReOOduHIM4NtOjWboA/kZb9y2L APkKH7MOuV4Ux56SFBAYnrmMf96dDrQf0z+dbq/M= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 16/25] firmware: arm_scmi: Add Telemetry debugfs SHMTI dump support Date: Sun, 20 Sep 2026 10:19:19 +0100 Message-ID: <20260920091928.2014972-17-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" Expose one debugfs entry for each discovered SHMTI that can be used to dump the related SHMTI in binary form from TBGN up to TEND markers. No processing is done kernel side, beside using proper accessors for device memory. Signed-off-by: Cristian Marussi --- v11 --> v12 - fix debugfs shmti dump file to use the SID as name v8 --> v9 - use kvzalloc/kfree - use file_update_time to update debugfs file ts v5 --> v6 - added mutex to protect binary dump buffer during reads (Sashiko) - using debugfs_create_file_size() (Sashiko) --- drivers/firmware/arm_scmi/telemetry.c | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/drivers/firmware/arm_scmi/telemetry.c b/drivers/firmware/arm_s= cmi/telemetry.c index ada4838d2351..7c1da722e0c3 100644 --- a/drivers/firmware/arm_scmi/telemetry.c +++ b/drivers/firmware/arm_scmi/telemetry.c @@ -11,8 +11,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -407,6 +410,14 @@ struct telemetry_shmti { struct scmi_telemetry_shmti_info info; }; =20 +struct scmi_telemetry_dbg_shmti { + struct telemetry_shmti *shmti; + size_t buf_sz; + void *buf; + /* Protect this descriptor from concurrent readers using it */ + struct mutex mtx; +}; + #define SHMTI_EPLG(s) \ ({ \ struct telemetry_shmti *_s =3D (s); \ @@ -1506,6 +1517,87 @@ scmi_telemetry_enumerate_common_intervals(struct tel= emetry_info *ti) ti->info.intervals); } =20 +static int scmi_telemetry_dbg_shmti_open(struct inode *inode, struct file = *filp) +{ + struct scmi_telemetry_dbg_shmti *sblob; + struct telemetry_shmti *shmti; + + if (!inode->i_private) + return -ENODEV; + + shmti =3D inode->i_private; + sblob =3D kzalloc_obj(*sblob); + if (!sblob) + return -ENOMEM; + + sblob->shmti =3D shmti; + sblob->buf =3D kvzalloc(shmti->info.len, GFP_KERNEL); + if (!sblob->buf) { + kfree(sblob); + return -ENOMEM; + } + + mutex_init(&sblob->mtx); + + filp->private_data =3D sblob; + + return 0; +} + +static ssize_t scmi_telemetry_dbg_shmti_read(struct file *filp, char __use= r *buf, + size_t count, loff_t *ppos) +{ + struct scmi_telemetry_dbg_shmti *sblob =3D filp->private_data; + + /* Dump on first read and again after each rewind to start pos */ + guard(mutex)(&sblob->mtx); + if (!sblob->buf_sz || *ppos =3D=3D 0) { + memcpy_fromio(sblob->buf, sblob->shmti->base, + sblob->shmti->info.len); + sblob->buf_sz =3D sblob->shmti->info.len; + /* update inode timestamps */ + file_update_time(filp); + } + + return simple_read_from_buffer(buf, count, ppos, sblob->buf, sblob->buf_s= z); +} + +static int scmi_telemetry_dbg_shmti_release(struct inode *inode, struct fi= le *filp) +{ + struct scmi_telemetry_dbg_shmti *sblob =3D filp->private_data; + + kvfree(sblob->buf); + kfree(sblob); + + return 0; +} + +static const struct file_operations scmi_telemetry_dbg_shmti_fops =3D { + .open =3D scmi_telemetry_dbg_shmti_open, + .release =3D scmi_telemetry_dbg_shmti_release, + .read =3D scmi_telemetry_dbg_shmti_read, + .llseek =3D generic_file_llseek, + .owner =3D THIS_MODULE, +}; + +static void scmi_telemetry_debugfs_initialize(struct telemetry_info *ti) +{ + const struct scmi_protocol_handle *ph =3D ti->ph; + struct dentry *top, *shmti_top; + + top =3D ph->hops->debugfs_proto_dentry_get(ph); + shmti_top =3D debugfs_create_dir("shmtis", top); + + for (unsigned int i =3D 0; i < ti->num_shmti; i++) { + char id[16]; + + snprintf(id, 16, "%u", ti->shmti[i].info.sid); + debugfs_create_file_size(id, 0444, shmti_top, &ti->shmti[i], + &scmi_telemetry_dbg_shmti_fops, + ti->shmti[i].info.len); + } +} + static int iter_shmti_update_state(struct scmi_iterator_state *st, const void *response, void *priv) { @@ -4119,6 +4211,9 @@ static int scmi_telemetry_protocol_init(const struct = scmi_protocol_handle *ph) } } =20 + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_PROTOCOLS)) + scmi_telemetry_debugfs_initialize(ti); + return 0; } =20 --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E6E2D3F1640; Sun, 20 Sep 2026 09:21:23 +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=1789896086; cv=none; b=Sf+wwsVOCG7EC48+MHddQfVTDjmbJ8SliqwSHzoSeX4D0Jhdee5ZNmEHHKVhYYEuiOKCfXY/wpEKcHHuP+AsWkiEtmixOVDRKJHzdtnxW6GQRM8kWa5V9VKRMWfV7coR552D6whkd20WTkPHIO5i71oa3v6yG7UbOpGKA76dkmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896086; c=relaxed/simple; bh=w7L8DbnoCEuyvO8JUAes+aM9b0PjMj4j60HnlNcmEQg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bbOzCPnQXVEUm6536mlNpRZA4bIxBgJ9s/lW8u6m8bj9XIPDJ6KvDnXCmHQFJ7lYQ1bfBMa+P3UunFPdCJDSGjzf7PqSZ4dRST1uAkB+ZGdUePjMJi1f6coZU2AT3Yte4mCWn9zJxHEpw1IfgDhvHI47ZmSa34Wa9GnoaQCX9Ks= 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=Edb9sFg1; 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="Edb9sFg1" 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 695881650; Sun, 20 Sep 2026 02:21:19 -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 087FE3F59E; Sun, 20 Sep 2026 02:21:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896082; bh=w7L8DbnoCEuyvO8JUAes+aM9b0PjMj4j60HnlNcmEQg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Edb9sFg1euS2DnDbeahHVBjeguj1hf22L0sqgheDLp/UvgSAvzTh4ETmwIX2srYrR B+Ymw+J4YXNokzIvfQGscclTX8O5b2rmt2kKmr5UTLUlsnsIjcJYWIaVeQqzKseCzr bTljuYb0f+IuhhQfNoEC0qVpkR45zWBxVhldpmKQ= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 17/25] firmware: arm_scmi: Add Telemetry debugfs ABI documentation Date: Sun, 20 Sep 2026 10:19:20 +0100 Message-ID: <20260920091928.2014972-18-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 description of the debugfs SCMI Telemetry protocol ABI. Signed-off-by: Cristian Marussi --- v5 --> v6 - fixed typos --- Documentation/ABI/testing/debugfs-scmi | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/ABI/testing/debugfs-scmi b/Documentation/ABI/tes= ting/debugfs-scmi index ee7179ab2edf..edbbe360321d 100644 --- a/Documentation/ABI/testing/debugfs-scmi +++ b/Documentation/ABI/testing/debugfs-scmi @@ -68,3 +68,25 @@ Description: Max number of concurrently allowed in-fligh= t SCMI messages for the currently configured SCMI transport for instance on the RX channels. Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi//protocols/0x/ +Date: Nov 2026 +KernelVersion: 7.3 +Contact: cristian.marussi@arm.com +Description: A subdirectory grouping debug entries related to protocol + for instance . Each protocol owns and defines the subtree + of entries rooted under this directory. +Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi//protocols/0x1B/shmtis/ +Date: Nov 2026 +KernelVersion: 7.3 +Contact: cristian.marussi@arm.com +Description: A set of RO files exposed by the Telemetry protocol (0x1B) in + order to dump the latest snapshot of each SHMTI memory area in + binary format: each file is named by its SHMTI id. + File is seekable and a seek to position zero on an open file + causes the SHMTI snapshot to be refreshed; file timestamps are + updated after each snapshot. + This directory reports SHMTIs for instance . +Users: Debugging, any userspace test suite --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CDA433F4DC5; Sun, 20 Sep 2026 09:21:27 +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=1789896089; cv=none; b=B8XBhZqaShU1i0ROM6s9omtkYvL0NQqHFGhHbVXVJ85sXhreIKPIFp0L/emt/LLyu73Uhyg+YGqlaMRJIz5VgsM546rnjf2+r2mzXL5SDPs9GyLHRDt0bjC+y4jmojvcF1BM5u4xiNrnU8M7Lutpzk5LCb+CpBtcvFPuXAxBNZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896089; c=relaxed/simple; bh=O0cFYabobz2jqFk60laht8vyGhpYDGlxJ5P/lorbBzw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SXibKXWTe5jgaIvZViOyBWVA/A2zfBFy/pX+hdDZXUEie1Cph1/ns/BlpK+Vfcq55FDgKwYBezmWGEweEKi39s/T1TDEhmhcvU9VHvCWwmY8cNbV85RTcSu6c746Uuje8xxgSF5/3My6OPMMk2nhnjqOEI7ixdAJhHm4FRXWX+0= 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=ZP4RfplE; 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="ZP4RfplE" 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 AFE581691; Sun, 20 Sep 2026 02:21:23 -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 4E55F3F59E; Sun, 20 Sep 2026 02:21:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896087; bh=O0cFYabobz2jqFk60laht8vyGhpYDGlxJ5P/lorbBzw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZP4RfplEjlDkzsVgehkQFCgwuWZSIRw5xoLYzrZEwFVDApcQelCymsBOVr38NFzCL UqtEjRCPE8EqUMedz5rFKJ/fh4xcFEDoNX+RiTqpNGGtD60z/XX1B+r/26SC002vu9 0GfUQ6tdawv7odOWpYYy0OJSiwazdT0N99FlAuNU= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 18/25] firmware: arm_scmi: Expose per-instance identifier Date: Sun, 20 Sep 2026 10:19:21 +0100 Message-ID: <20260920091928.2014972-19-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" When multiple SCMI instances are configured, the SCMI stack probes and it is initialized multiple times: one core stack for each defined instance. Each istance is simply identified internally with a number and such number is already exposed to the user to name the root of the per-instance debugfs subtree, if enabled. Expose such unique identifier to the SCMI drivers so that they can use that same identifier when in need to name their per-instance resources. Signed-off-by: Cristian Marussi --- v7 --> v8 - fixed Doxygen 'non-negative' integer --- drivers/firmware/arm_scmi/driver.c | 1 + include/linux/scmi_protocol.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi= /driver.c index 8bb5e4c1bbbc..f5aca46bc608 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3352,6 +3352,7 @@ static int scmi_probe(struct platform_device *pdev) idr_init(&info->rx_idr); =20 handle =3D &info->handle; + handle->id =3D info->id; handle->dev =3D info->dev; handle->version =3D &info->version; handle->devm_protocol_acquire =3D scmi_devm_protocol_acquire; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 6b1b0eee2651..4b44c9f503bc 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1125,6 +1125,10 @@ struct scmi_notify_ops { /** * struct scmi_handle - Handle returned to ARM SCMI clients for usage. * + * @id: A unique non-negative natural integer identifying the SCMI Instance + * associated with this handle to be used across all drivers for + * naming purposes: same identifier used internally as the root for + * the debugfs per-instance tree. * @dev: pointer to the SCMI device * @version: pointer to the structure containing SCMI version information * @devm_protocol_acquire: devres managed method to get hold of a protocol, @@ -1145,6 +1149,7 @@ struct scmi_notify_ops { * @notify_ops: pointer to set of notifications related operations */ struct scmi_handle { + unsigned int id; struct device *dev; struct scmi_base_info *version; =20 --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 02FAA3E959C; Sun, 20 Sep 2026 09:21:32 +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=1789896093; cv=none; b=c94yllDdM/7BNRJMiLR58gXtC1NAxnYWFayTYUmuyf83sb0igV8c6ACWW9lhfefbKy1wtJgIqDW1AEPo1m4KpGSeqqtEC11KZkYT7Hf0qAzNHWOunUEKQdKyS+f31K6RBXxlBcrDAC8F/IYDgaW5M3KpxtQ3Ngad6KcuAmtIv1s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896093; c=relaxed/simple; bh=HnEcRC1YRZYszSkF2yhcRqz//Sgrtd8Yokg4b6YTq58=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j9x3VdJnJb6aC/5gzabUEkeProLCtjlTTL3XT3FjymS/pm/YIMdmy3+GY91WXLIQK8vFn+ip7XgiP8X6LKEbOADAMDG7rcXWJLErF3ZUYBuqaydDfIVKLd2df4PBPsqI2nUx60qVzdSvLSwovKWMoCZnffUicJOCJNTtphiD8aA= 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=lVYscqX/; 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="lVYscqX/" 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 EF3D01650; Sun, 20 Sep 2026 02:21:27 -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 9234A3F59E; Sun, 20 Sep 2026 02:21:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896091; bh=HnEcRC1YRZYszSkF2yhcRqz//Sgrtd8Yokg4b6YTq58=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lVYscqX/oD8fcYPyMUWyaQiA0wnpuhETquewmMkgfn8SyjD3Xu8T2sGSj1RfIs4pG oweYH/8YtX3y33ZSob8Nx6OGnUEJ4+k33jlH9KD4D4uE4Mni8V06GkLPvFzj4bwC3e FMi6HfBFLNmX1WVLBjX3KbLPOXRPyqsxm0/Rkxkc= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 19/25] firmware: arm_scmi: Add un-managed methods to get/put protocols operations Date: Sun, 20 Sep 2026 10:19:22 +0100 Message-ID: <20260920091928.2014972-20-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 a couple of simple common methods to be able to manually get and put protocol operations to address situation in which devres managed methods cannot be used. Signed-off-by: Cristian Marussi --- v10 --> v11 - fixed a few comments typos --- drivers/firmware/arm_scmi/driver.c | 39 ++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 7 ++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi= /driver.c index f5aca46bc608..6838a1d5f80a 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2571,6 +2571,31 @@ scmi_devm_protocol_get(struct scmi_device *sdev, u8 = protocol_id, return pi->proto->ops; } =20 +/** + * scmi_protocol_operations_get - UnManaged get protocol operations + * + * @handle: A reference to the SCMI platform instance. + * @protocol_id: The protocol being acquired. + * @ph: A pointer reference used to pass back the associated protocol hand= le. + */ +static const void __must_check * +scmi_protocol_operations_get(const struct scmi_handle *handle, u8 protocol= _id, + struct scmi_protocol_handle **ph) +{ + struct scmi_protocol_instance *pi; + + if (!ph) + return ERR_PTR(-EINVAL); + + pi =3D scmi_get_protocol_instance(handle, protocol_id); + if (IS_ERR(pi)) + return pi; + + *ph =3D &pi->ph; + + return pi->proto->ops; +} + /** * scmi_devm_protocol_acquire - Devres managed helper to get hold of a pr= otocol * @sdev: A reference to an scmi_device whose embedded struct device is to @@ -2627,6 +2652,18 @@ static void scmi_devm_protocol_put(struct scmi_devic= e *sdev, u8 protocol_id) WARN_ON(ret); } =20 +/** + * scmi_protocol_operations_put - UnManaged put protocol operations + * + * @handle: A reference to the SCMI platform instance. + * @protocol_id: The protocol being released. + */ +static void scmi_protocol_operations_put(const struct scmi_handle *handle, + u8 protocol_id) +{ + scmi_protocol_release(handle, protocol_id); +} + /** * scmi_is_transport_atomic - Method to check if underlying transport for= an * SCMI instance is configured as atomic. @@ -3358,6 +3395,8 @@ static int scmi_probe(struct platform_device *pdev) handle->devm_protocol_acquire =3D scmi_devm_protocol_acquire; handle->devm_protocol_get =3D scmi_devm_protocol_get; handle->devm_protocol_put =3D scmi_devm_protocol_put; + handle->protocol_get =3D scmi_protocol_operations_get; + handle->protocol_put =3D scmi_protocol_operations_put; handle->is_transport_atomic =3D scmi_is_transport_atomic; =20 /* Setup all channels described in the DT at first */ diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 4b44c9f503bc..ebe3ee6e7aaa 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1137,6 +1137,9 @@ struct scmi_notify_ops { * @devm_protocol_get: devres managed method to acquire a protocol and get= specific * operations and a dedicated protocol handler * @devm_protocol_put: devres managed method to release a protocol + * @protocol_get: unmanaged method to acquire a protocol and get specific + * operations and a dedicated protocol handler + * @protocol_put: unmanaged method to release a protocol * @is_transport_atomic: method to check if the underlying transport for t= his * instance handle is configured to support atomic * transactions for commands. @@ -1159,6 +1162,10 @@ struct scmi_handle { (*devm_protocol_get)(struct scmi_device *sdev, u8 proto, struct scmi_protocol_handle **ph); void (*devm_protocol_put)(struct scmi_device *sdev, u8 proto); + const void __must_check * + (*protocol_get)(const struct scmi_handle *handle, u8 proto, + struct scmi_protocol_handle **ph); + void (*protocol_put)(const struct scmi_handle *handle, u8 proto); bool (*is_transport_atomic)(const struct scmi_handle *handle, unsigned int *atomic_threshold); const struct scmi_notify_ops *notify_ops; --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6AB903F5BE4; Sun, 20 Sep 2026 09:21:36 +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=1789896098; cv=none; b=iSIdbXyoH2xrS4zMvrKc8m1Mq4hrZe902e8LTzNKEKMvROcUINOGGzWcx4/uRnvLjYZxbld4uHl5uKQlZLS9gnrgfZf8F2TbvTmb1mfZTrj6V7MJ5koG3wiEqB+tUE7OBkKhS/YGpRoCpjjpLx7jFcl+qXRLZeleVpseOqa6fTI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896098; c=relaxed/simple; bh=XXweVw/0aoVE1195gcW/lp8aateUwN+Xgsm+3+inVac=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ms3Tflo4bAu1purQ/7lkDdg++j6R0HvXhFV+7TCyl/4oG8Wh1rpoGRApFptC0aKDbVc967zjBQmFyWqxDPP/dpaYPgI/xYSvHg/ru53pbJZBxzdYGiP5ar6Zx//PsolQOkiP13wcHApMbj37trbwymuMprKZ4dS1nImrnt1C/ro= 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=tO0OJedn; 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="tO0OJedn" 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 4D94E1691; Sun, 20 Sep 2026 02:21:32 -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 D71413F59E; Sun, 20 Sep 2026 02:21:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896095; bh=XXweVw/0aoVE1195gcW/lp8aateUwN+Xgsm+3+inVac=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tO0OJedna2+FcQcQKT8rkASlRc0+EYcBra+D7Pz0iwtFmYvzOi4CKBARYgPWnubhr Zze/4an+I5UGBBtYF4x3Ygu4LLqz2AFigygi8JaaYj5D3zvnN0KWyj+OLYBsLmo1zZ RQB5k7/XVgO2ipMm1WKyQZ+otOIbw+ub+MjUOArQ= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 20/25] uapi: Add ARM SCMI Telemetry definitions Date: Sun, 20 Sep 2026 10:19:23 +0100 Message-ID: <20260920091928.2014972-21-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 a number of structures and ioctls definitions used by the ARM SCMI Telemetry protocol. Signed-off-by: Cristian Marussi --- v11 --> v12 - fixed documentation around event lifetime cycle to align with the new implemented logic v10 --> v11 - typos fixing and comments fixup - more docs on BATCH IOCTLs and structures - more docs on SHMTI_LIST - more docs on EVENT_REGISTER - more docs on GET_INTRVS upper bounds and zero-query - more docs on BULK/SINGLE upper bounds and zero-query v8 --> v9 - Simplified ABI related to _LIST IOCTL, substituting all custom container structures in favour of the usage of scmi_tlm_batch, i.e. dropping: - scmi_tlm_des_list - scmi_tlm_uuid_list - scmi_tlm_grps_list - scmi_tlm_shmtis_list - Updated related _LIST documentation v7 --> v8 - Fixes for some Sashiko issues on v7 - fix typos and wrong struct reference in SCMI_TLM_BATCH_READ - move a couple of IOCTL to be _IOW - add a few more comments on usage and behaviour of BATCH ops v6 --> v7 - added per-DE UUID/SID/OFFS - IOCTL EVENTS: generation counter - generic BATCH support (scmi_tlm_batch) - BATCH READ with perf-DE status - BATCH DE GET/SET CONFIG - added reserved fields for grow - added more ABI features flags - better Doxygen desciption for IN/OUT params v5 --> v6 - Fixed interval exp to be __s32 as documented - Using a more unique name for IOCTL MAGIC define - Renamed a few IOCTLs to be consistent - redefine primary UUID as an array of u8 - add GET_ABI_INFO to gather ABI version and instance info - add RESET support - fixed typos in kdoc v4 --> v5 - Added more structure padding - Removed frowned upon flexible array in favour of u64 pointers to external userspace provided buffer - Added UAPI support to access SHMTIs from userspace - Cleaned up Doxygen v2 --> v3 - Change tstamp_exp tp ts_rate - renamed num -> num_intervals in scmi_tlm_interval - added padding in scmi_tlm_de_sample to avoid packing issues on 32bit v1 --> v2 - Added proper __counted_by marks - Fixed a few dox comments - Renamed reserved[] fields to pad[] --- MAINTAINERS | 1 + include/uapi/linux/scmi.h | 522 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100644 include/uapi/linux/scmi.h diff --git a/MAINTAINERS b/MAINTAINERS index c2414447892c..81e288e5de11 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26606,6 +26606,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 =20 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..41fa2db9f1d6 --- /dev/null +++ b/include/uapi/linux/scmi.h @@ -0,0 +1,522 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2026 ARM Ltd. + */ +#ifndef _UAPI_LINUX_SCMI_H +#define _UAPI_LINUX_SCMI_H + +/* + * Userspace interface SCMI Telemetry + */ + +#include +#include + +#define SCMI_TLM_ABI_VERSION_V1 1 +#define SCMI_TLM_CURRENT_ABI_VERSION SCMI_TLM_ABI_VERSION_V1 +#define SCMI_TLM_DE_IMPL_UUID_SZ 16 + +#define SCMI_TLM_SHMTI_ID_INVALID 0xFFFFFFFF + +/** + * struct scmi_tlm_abi_info - Basic information on the ABI/instance + * + * SCMI Telemetry characteristics like number of DEs, Groups, Intervals and + * SHMTIs are statically determined by the platform and do not change with= in + * one boot/session; they are reported here so that they can be used to pr= operly + * size the requests on other IOCTLs, without having to discover dynamical= ly + * those sizes by issuing a zero-sized IOCTL request at first. + * + * @size: sizeof this structure - IN + * @abi_version: ABI Version - OUT + * @abi_features: ABI capabilities bitmap - OUT + * @primary_de_impl_version: SCMI Telemetry DE implementation revision - O= UT + * @num_des: Number of defined DEs - OUT + * @num_groups: Number of defined DEs groups - OUT + * @num_intervals: Number of available update intervals (instance-level) -= OUT + * @num_shmtis: Number of discovered SHMTI areas - OUT + * @features: Instance specific SCMI feature-support bitmap - OUT + * @reserved: Some room for future expansion without the need to resize. + * Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_ABI_INFO + */ +struct scmi_tlm_abi_info { + __u32 size; + __u32 abi_version; + __u32 abi_features; +#define SCMI_TLM_ABI_FEAT_RESET (1 << 0) +#define SCMI_TLM_ABI_FEAT_EVENT (1 << 1) +#define SCMI_TLM_ABI_FEAT_UUID_LIST (1 << 2) +#define SCMI_TLM_ABI_FEAT_BATCH_STATE (1 << 3) +#define SCMI_TLM_ABI_FEAT_BATCHED_CFG (1 << 4) +#define SCMI_TLM_ABI_FEAT_DE_TRACKING (1 << 5) + __u8 primary_de_impl_version[SCMI_TLM_DE_IMPL_UUID_SZ]; + __u32 num_des; + __u32 num_groups; + __u32 num_intervals; + __u32 num_shmtis; + __u32 features; +#define SCMI_TLM_SCMI_SUPPORT_RESET (1 << 0) +#define SCMI_TLM_SCMI_SUPPORT_SINGLE_SAMPLE (1 << 1) +#define SCMI_TLM_SCMI_SUPPORT_GROUP_CONFIG (1 << 2) +#define SCMI_TLM_SCMI_SUPPORT_UPDATE_NOTIFICATION (1 << 3) + __u64 reserved; +}; + +/** + * struct scmi_tlm_update_interval - Update interval descriptor + * + * @secs: integer representing seconds - OUT + * @exp: signed integer representing the base 10 exponent used as a multip= lier + * with @secs to represent the interval - OUT + * + * The resulting update interval is calculated as: * 10 ^ + */ +struct scmi_tlm_update_interval { + __u32 secs; + __s32 exp; +}; + +/** + * struct scmi_tlm_config - Whole instance or group configuration + * + * @enable: Get/Set Telemetry enabled state for the whole instance or the + * group specified in @grp_id - IN/OUT + * @t_enable: Get/Set timestamp state for the group specified in @grp_id -= IN/OUT + * @flags: Bitmask to represent special characteristics - IN + * @pad: Padding fields to enforce alignment + * @grp_id: Identifier of the target group upon which this configuration w= ill + * be applied: ignored if not marked as a group in @flags - IN/OUT + * @active: Get/Set currently active update interval for the whole instance + * or the group specified in @grp_id - IN/OUT + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_CFG + * WO - SCMI_TLM_SET_CFG + */ +struct scmi_tlm_config { + __u8 enable; + __u8 t_enable; + __u8 flags; +#define SCMI_TLM_CONFIG_GROUP (1 << 0) +#define SCMI_TLM_CONFIG_FLAGS (SCMI_TLM_CONFIG_GROUP) +#define SCMI_TLM_CONFIG_IS_GROUP(f) ((f) & SCMI_TLM_CONFIG_GROUP) + __u8 pad; + __u32 grp_id; + struct scmi_tlm_update_interval active; + __u64 reserved; +}; + +/** + * struct scmi_tlm_intervals - Update intervals descriptor + * + * @grp_id: Identifier of the target group upon which this configuration + * will be applied: ignored if not marked as a group in @flags - IN + * @flags: Bitmask to represent special characteristics. When the interval + * is NOT marked as SCMI_TLM_INTERV_DISCRETE, @intervals will + * contain a triplet: min/max/step - OUT + * @pad: Padding fields to enforce alignment + * @num_intervals: Number of entries of @intervals. On input this represen= ts the + * size of @intervals, while on output carries the effective + * number of entries filled-in. + * On input this has an upper-bound determined as: + * - @scmi_tlm_abi_info.num_intervals if NOT referring a group + * OR + * - @scmi_tlm_grp_info.num_intervals if referring a group + * When provided as zero it will return the upper-bound itself + * leaving @intervals untouched - IN/OUT + * @pad2: Padding fields to enforce alignment + * @reserved: Some room for future expansion. Must be zero. + * @intervals: A pointer to an array of struct scmi_tlm_update_interval - = IN/OUT + * + * Used by: + * RW - SCMI_TLM_GET_INTRVS + */ +struct scmi_tlm_intervals { + __u32 grp_id; + __u8 flags; +#define SCMI_TLM_INTERV_GROUP (1 << 0) +#define SCMI_TLM_INTERV_DISCRETE (1 << 1) +#define SCMI_TLM_INTERV_FLAGS \ + (SCMI_TLM_INTERV_GROUP | SCMI_TLM_INTERV_DISCRETE) +#define SCMI_TLM_INTERV_IS_GROUP(f) ((f) & SCMI_TLM_INTERV_GROUP) + __u8 pad[3]; + __u32 num_intervals; + __u32 pad2; + __u64 reserved; +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_LOW 0 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_HIGH 1 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_STEP 2 + __u64 intervals; +}; + +/** + * struct scmi_tlm_de_config - DE configuration + * + * This descriptor is used directly by the aggregate SCMI_TLM_GET_ALL_CFG = and + * SCMI_TLM_SET_ALL_CFG IOCTLs, or embedded into an @scmi_tlm_batch reques= t for + * SCMI_TLM_GET_DE_CFG and SCMI_TLM_SET_DE_CFG IOCTLs. + * + * @id: Identifier of the DE, ignored by SCMI_TLM_GET/SET_ALL_CFG - IN + * @enable: Get/Set the enabled state of this single DE or the cumulative = ALL + * state - IN/OUT + * @t_enable: Get/Set the timestamp state of this single DE (if supported)= or + * the cumulative ALL state - IN/OUT + * @sid: An integer representing the SHMTI ID that contains this DE. Valid + * ONLY when the related DE is enabled - OUT + * @offset: An integer representing the offset in the SHMTI @sid that iden= tifies + * the start of the TDCF DataLine containing this DE. + * Valid ONLY when the related DE is enabled - OUT + * @pad: Padding fields to enforce alignment + * @uuid: An array containing the UUID, in BigEndian format, associated to + * this DE. Valid ONLY when the related DE is enabled: this UUID can + * be the primary UUID or one of the secondary UUIDs if any exist. + * Note that this UUID/DE association is permanent within the same + * boot/session - OUT + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_ALL_CFG + * WO - SCMI_TLM_SET_ALL_CFG + * RW - SCMI_TLM_GET_DE_CFG (within a BATCH) + * RW - SCMI_TLM_SET_DE_CFG (within a BATCH) + */ +struct scmi_tlm_de_config { + __u32 id; + __u32 enable; + __u32 t_enable; + __u32 sid; + __u32 offset; + __u32 pad; + __u8 uuid[SCMI_TLM_DE_IMPL_UUID_SZ]; + __u64 reserved; +}; + +/** + * struct scmi_tlm_de_info - DataEvent Descriptor + * + * @id: DE identifier - IN/OUT + * @grp_id: Identifier of the group which this DE belongs to; valid only if + * this DE is marked as belonging to a group in @flags - IN/OUT + * @data_sz: DE data size in bytes - OUT + * @type: DE type - OUT + * @unit: DE unit of measurements - OUT + * @unit_exp: Power-of-10 multiplier for DE unit - OUT + * @ts_rate: Clock rate in kHz used to generate the DE timestamp - OUT + * @instance_id: DE instance ID - OUT + * @compo_instance_id: DE component instance ID - OUT + * @compo_type: Type of component which is associated to this DE - OUT + * @persistent: Data value for this DE survives reboot (non-cold ones) - O= UT + * @flags: Bitmask to represent special characteristics - IN + * @pad: Padding fields to enforce alignment + * @pad2: Padding fields to enforce alignment + * @name: Name of this DE - OUT + * @reserved: Some room for future expansion. Must be zero. + * + * Used to get the full description of a DE: it reflects DE Descriptors + * definitions in SCMI V4.0 specification at 3.12.4.6. + * + * Used by: + * RW - SCMI_TLM_GET_DE_INFO + * RW - SCMI_TLM_GET_DE_LIST (within a @scmi_tlm_batch) + */ +struct scmi_tlm_de_info { + __u32 id; + __u32 grp_id; + __u32 data_sz; + __u32 type; + __u32 unit; + __s32 unit_exp; + __u32 ts_rate; + __u32 instance_id; + __u32 compo_instance_id; + __u32 compo_type; + __u8 persistent; + __u8 flags; +#define SCMI_TLM_DEINFO_GROUP (1 << 0) +#define SCMI_TLM_DEINFO_FLAGS (SCMI_TLM_DEINFO_GROUP) +#define SCMI_TLM_DEINFO_HAS_GROUP(f) ((f) & SCMI_TLM_DEINFO_GROUP) + __u8 pad[2]; + __u32 pad2; + __u8 name[16]; + __u64 reserved; +}; + +/** + * struct scmi_tlm_de_sample - A DataEvent reading + * + * @id: DE identifier - IN + * @pad: Padding fields to enforce alignment. + * @tstamp: DE reading timestamp (0 if timestamp NOT supported) - OUT + * @val: Reading of the DE data value - OUT + * + * Used by: + * RW - SCMI_TLM_DE_READ + * RW - SCMI_TLM_BATCH_READ (within an @scmi_tlm_batch) + */ +struct scmi_tlm_de_sample { + __u32 id; + __u32 pad; + __u64 tstamp; + __u64 val; +}; + +/** + * struct scmi_tlm_data_read - Bulk read of a number of DataEvents + * + * @grp_id: Optional group ID number, ignored if not marked as a group req= uest + * in @flags and not supported by SCMI_TLM_BATCH_READ - IN + * @flags: Bitmask to represent special characteristics - IN + * @pad: Padding fields to enforce alignment + * @pad2: Padding fields to enforce alignment + * @num_samples: Number of entries in @samples. In input represents the si= ze of + * @samples, while in output carries the effective number of items + * filled-in. + * On input this has an upper-bound determined as: + * - @scmi_tlm_abi_info.num_des if NOT referring a group + * OR + * - @scmi_tlm_grp_info.num_des if referring a group + * When provided as zero it will return the upper-bound itself + * leaving @samples untouched - IN/OUT + * @samples: A reference to an array of struct scmi_tlm_de_sample containi= ng + * an entry for each DE - IN/OUT + * + * Used by: + * RW - SCMI_TLM_SINGLE_READ + * RW - SCMI_TLM_BULK_READ + */ +struct scmi_tlm_data_read { + __u32 grp_id; + __u8 flags; +#define SCMI_TLM_READ_GROUP (1 << 0) +#define SCMI_TLM_READ_FLAGS (SCMI_TLM_READ_GROUP) +#define SCMI_TLM_READ_IS_GROUP(f) ((f) & SCMI_TLM_READ_GROUP) + __u8 pad[3]; + __u32 pad2; + __u32 num_samples; + __u64 samples; +}; + +/** + * struct scmi_tlm_batch - A wrapper structure for BATCH operations + * + * This structure is used as an opaque container for a set of objects of t= he + * same kind: it is used by a number of different IOCTLs and it is up to t= he + * specific IOCTL implementation to enforce a proper upper-bound on the + * maximum amount of items that can be specified in @num_items. + * + * Exceeding the IOCTL-specific upper bound in @num_items leads to an error + * (-EINVAL), while issuing an IOCTL requesting in @num_items less than the + * related maximum upper-bound it will succeed and simply return only the + * required @num_items. + * + * It is possible to discover each specific upper-bound in a couple of + * alternative ways: + * + * - by issuing a ZERO sized query IOCTl at first (as detailed down below) + * for any of the IOCTLs that use the @scmi_tlm_batch parameter. + * + * - by querying in advance, once for all, the platform to retrieve all the + * statically defined resource counters with SCMI_TLM_GET_ABI_INFO. + * + * The per-IOCTL statically defined upper-bounds are defined as follows: + * + * - SCMI_TLM_BATCH_READ: scmi_tlm_abi_info.num_des + * - SCMI_TLM_GET_DE_CFG: scmi_tlm_abi_info.num_des + * - SCMI_TLM_SET_DE_CFG: scmi_tlm_abi_info.num_des + * - SCMI_TLM_GET_DE_LIST: scmi_tlm_abi_info.num_des + * - SCMI_TLM_GET_GRP_LIST: scmi_tlm_abi_info.num_groups + * - SCMI_TLM_GET_SHMTI_LIST: scmi_tlm_abi_info.num_shmtis + * + * The @states field is optional and when it is NULL it is up to the speci= fic + * IOCTL implementation to decide which policy to enforce in case of failu= res + * in the middle of the batch processing: as of now each BATCHED IOCTLs fa= ils + * as a whole as soon as one of the batched requests fails, without any ki= nd + * of roll-back of the previously succeeded requests. + * + * When this container structure is used as parameter for one the _LIST + * IOCTLs listed below, @states is ignored in any case, and the @num_items + * field can be set to zero in input to query the system about the maximum + * number of available resources. + * Note that this idiomatic query-style is made available as an alternative + * usage pattern, but somr of this information is usually statically defin= ed + * within the same session/boot and available by other means (IOCTLs). + * + * The field @item_sz MUST match the expected payload size for the specific + * IOCTL and it is supposed to be meaningfully set ALSO when @num_items is + * zero. + * + * @num_items: Number of items in @items and @states - IN/OUT + * @item_sz: Size of a single item contained in @items - IN + * @reserved: Some room for future expansion. Must be zero. + * @states: An optional reference to an array of s32 items representing t= he + * outcome of the requests for each single item in @items: these are + * ordered in the same order as the @items. - OUT + * @items: A reference to an array of batched requests - IN/OUT + * + * Used by: + * RW - SCMI_TLM_BATCH_READ - container of @scmi_tlm_de_sample + * RW - SCMI_TLM_GET_DE_CFG - container of @scmi_tlm_de_config + * RW - SCMI_TLM_SET_DE_CFG - container of @scmi_tlm_de_config + * RW - SCMI_TLM_GET_DE_LIST - container of @scmi_tlm_de_info + * RW - SCMI_TLM_GET_UUID_LIST - container of @scmi_tlm_uuid + * RW - SCMI_TLM_GET_GRP_LIST - container of @scmi_tlm_grp_info + * RW - SCMI_TLM_GET_SHMTI_LIST - container of @scmi_tlm_shmti_info + */ +struct scmi_tlm_batch { + __u32 num_items; + __u32 item_sz; + __u64 reserved; + __u64 states; + __u64 items; +}; + +/** + * struct scmi_tlm_grp_info - Group info descriptor + * + * @grp_id: Group ID number - IN + * @num_des: Number of DEs part of this group - OUT + * @num_intervals: Number of update intervals supported. Zero if group doe= s not + * support per-group update interval configuration. - OUT + * @pad: Padding fields to enforce alignment + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_GRP_INFO + * RW - SCMI_TLM_GET_GRP_LIST (within a BATCH) + */ +struct scmi_tlm_grp_info { + __u32 grp_id; + __u32 num_des; + __u32 num_intervals; + __u32 pad; + __u64 reserved; +}; + +/** + * struct scmi_tlm_grp_desc - Group descriptor + * + * @grp_id: Group ID number - IN + * @num_des: Number of DEs composing this group. In input represents the s= ize + * of @composing_des, while in output carries the effective number + * of items filled-in - IN/OUT + * @composing_des: A reference to an array of __u32 elements containing the + * DataEvent IDs composing this group. - IN/OUT + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_GRP_DESC + */ +struct scmi_tlm_grp_desc { + __u32 grp_id; + __u32 num_des; + __u64 composing_des; + __u64 reserved; +}; + +/** + * struct scmi_tlm_shmti_info - SHMTI descriptor + * + * @sid: SHMTI ID - IN + * @fd: Associated opened file descriptor to use for mmap. This file descr= iptor + * can only be used to mmap the SHMTIs to read-only pages and it is + * implicitly marked as O_CLOEXEC. The lifetime of any associated mapping + * obtained from @fd is independent from it and from the file descriptor + * used to issue the related IOCTL - OUT + * @len: Effective size of the accessible area of the SHMTI that will be m= apped + * using @fd. Note that, since mmap allows mapping length of multiple of + * PAGE_SIZE, this @len implicitly sets the upper-bound of the number of + * PAGE_SIZE pages that you are allowed to request - OUT + * @offset: Offset in the obtained mmap where the specified SHMTI start - = OUT + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_GET_SHMTI_LIST (within an @scmi_tlm_batch) + */ +struct scmi_tlm_shmti_info { + __u32 sid; + __u32 fd; + __u32 len; + __u32 offset; + __u64 reserved; +}; + +/** + * struct scmi_tlm_uuid - UUID descriptor + * + * @bytes: An array of bytes containing the UUID in BigEndian order as + * per RFC 9562. - OUT + * + * Used by: + * RW - SCMI_TLM_GET_UUID_LIST (within an @scmi_tlm_batch) + */ +struct scmi_tlm_uuid { + __u8 bytes[SCMI_TLM_DE_IMPL_UUID_SZ]; +}; + +/** + * struct scmi_tlm_event - Event descriptor + * + * @type: Type of Telemetry event to subscribe/unsubscribe - IN + * @efd: An integer representing an open eventfd file descriptor, which wi= ll + * be used to signal the subscribed event. The lifecycle of the event + * subscription is tied to the controlling chardev file descriptor NOT to + * the eventfd lifetime, so that if the file descriptor used to issue this + * IOCTL is closed all the events registered for that instance are cleare= d. + * Note that, though, if in the meantime the Telemetry driver is unloaded, + * the backing event subscription will be removed and the eventfd will + * remain orphaned. - IN + * @cookie: An integer used to represent a subscription request: ONLY posi= tive + * values represents valid subscription. + * On a new subscription this must be set to zero in input, to si= gnify + * that a new request is being made: if the subscription was + * successful a positive integer is returned. + * When, instead, @cookie is set to a positive integer in input, = it + * is interpreted as a reference to an existing subscription to be + * cancelled. Anybody with knowledge of the @cookie can cancel the + * subscription. + * @pad: Padding fields to enforce alignment + * @reserved: Some room for future expansion. Must be zero. + * + * Used by: + * RW - SCMI_TLM_EVENT_SUBSCRIBE + */ +struct scmi_tlm_event { +#define SCMI_TLM_EVT_GENERATION 0x0U +#define SCMI_TLM_EVT_LAST (SCMI_TLM_EVT_GENERATION) + __u32 type; + __u32 efd; + __u32 cookie; + __u32 pad; + __u64 reserved; +}; + +#define SCMI_TLM_IOCTL_MAGIC 0xF1 + +#define SCMI_TLM_GET_ABI_INFO _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x00, struct scm= i_tlm_abi_info) +#define SCMI_TLM_GET_CFG _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x01, struct scmi_tlm= _config) +#define SCMI_TLM_SET_CFG _IOW(SCMI_TLM_IOCTL_MAGIC, 0x02, struct scmi_tlm_= config) +#define SCMI_TLM_GET_INTRVS _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x03, struct scmi_= tlm_intervals) +#define SCMI_TLM_GET_DE_CFG _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x04, struct scmi_= tlm_batch) +#define SCMI_TLM_SET_DE_CFG _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x05, struct scmi_= tlm_batch) +#define SCMI_TLM_GET_DE_INFO _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x06, struct scmi= _tlm_de_info) +#define SCMI_TLM_GET_DE_LIST _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x07, struct scmi= _tlm_batch) +#define SCMI_TLM_DE_READ _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x08, struct scmi_tlm= _de_sample) +#define SCMI_TLM_GET_ALL_CFG _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x09, struct scmi= _tlm_de_config) +#define SCMI_TLM_SET_ALL_CFG _IOW(SCMI_TLM_IOCTL_MAGIC, 0x0A, struct scmi_= tlm_de_config) +#define SCMI_TLM_GET_GRP_LIST _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x0B, struct scm= i_tlm_batch) +#define SCMI_TLM_GET_GRP_INFO _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x0C, struct scm= i_tlm_grp_info) +#define SCMI_TLM_GET_GRP_DESC _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x0D, struct scm= i_tlm_grp_desc) +#define SCMI_TLM_SINGLE_READ _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x0E, struct scmi= _tlm_data_read) +#define SCMI_TLM_BULK_READ _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x0F, struct scmi_t= lm_data_read) +#define SCMI_TLM_BATCH_READ _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x10, struct scmi_= tlm_batch) +#define SCMI_TLM_GET_SHMTI_LIST _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x11, struct s= cmi_tlm_batch) +#define SCMI_TLM_RESET _IO(SCMI_TLM_IOCTL_MAGIC, 0x12) +#define SCMI_TLM_GET_UUID_LIST _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x13, struct sc= mi_tlm_batch) +#define SCMI_TLM_EVENT_SUBSCRIBE \ + _IOWR(SCMI_TLM_IOCTL_MAGIC, 0x14, struct scmi_tlm_event) + +#endif /* _UAPI_LINUX_SCMI_H */ --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CFCD73E5A2A; Sun, 20 Sep 2026 09:21:40 +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=1789896104; cv=none; b=fcwrmOdRh2SpVqjEJ493Pct9EEcEKcyYzy9fHl3mM5NXheLt2spqoMLxsEsnH5Vdb43+B8aG8OkofTujiA3Ku+gMIg3bjx6i6UOo0TYdCl4oxQStwL4q0I0nlFA6pszDmjVtoq/FRfUr+eVYpp0+Au3KW+Xjxxz1h7EagmT7vQ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896104; c=relaxed/simple; bh=56lZJ7cQSqVMQjZgLM2KT+uGwHvv4EhordS+Vyf9VQk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TNSretxcnYQ1k1sbDNupt9St6uuIKfrGO0r+ROgYAN5Qkos/3M3v59qRM4sSye8cY/+pf070ClRf39VzC2Oi+f2IN4dqGBXWZXLacGTv0KbRruoS2BLSsnFzqx1HesyTJBL/SAIyMKaAmwGnDgPhXIci/tXA8mymtnMA3jgDESU= 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=JOSt3pbs; 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="JOSt3pbs" 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 AB619175A; Sun, 20 Sep 2026 02:21:36 -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 2FCEA3F59E; Sun, 20 Sep 2026 02:21:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896100; bh=56lZJ7cQSqVMQjZgLM2KT+uGwHvv4EhordS+Vyf9VQk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JOSt3pbsJjA3XlPYHz43p3nUby7AG+QO2HHFGDTSbEsmkMfgTzgHvvaYACd6UOhpG 1Ukl9KwkCP9Ch74SH0soUsk0cIJFPawX+gnsiprp/zxRT6qD772Ikr7blYi6UxFmAp D6uJV6xarszV1IavMRr6QBj7fogiTvzRU28zHSPc= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 21/25] firmware: arm_scmi: Add System Telemetry driver Date: Sun, 20 Sep 2026 10:19:24 +0100 Message-ID: <20260920091928.2014972-22-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 an SCMI driver to allow enumeration, configuration and data collection of SCMI Telemetry data. The driver exposes one character device for each found SCMI instance and a set of related IOCTLs can be used to enumerate, configure and collect Telemetry resources. Signed-off-by: Cristian Marussi --- v11 --> v12 - added RCU deref access on UUIDs - dropped needless copy_from_user on states_batch - fixed double free on anon ids error path - reworked eventfd handling using per-session tracking and cleanup on chardev last release - properly free also ti->tsp on error path - properly acquire/release sdev->dev via get/put_device v10 --> v11 - make SCMI tlm_ devices 664 by default - add 0-sized query support for all the NON _LIST remaining BATCH ops - add 0-sized query support on GET_INTRVS - add 0-sized query support on BULOK/SINGLE reads v8 --> v9 - Fix some Sashiko reports on v8 - use kvmalloc/kvcalloc for all buffers with user/FW generated size - add a proper upper bounds checks on IOCTL requests - make SET_ALL_CFG carry on after a failure (reporting last error anyway) - properly initialize some possibly unused stack vars - refactor SHMTIs list processing and ANON fds allocation to avoid leaking fds by premature installing them into process. - completely rework cdev and scmi_tlm_instance interactions and lifecycle and drop all devres allocation: reject fops on cdev after driver unbind and delay data structures freeing till the last reference has been dropped. - reworked BATCH helpers to handle ZERO items requests (for IOCTL query) - reworked all the _LIST IOCTLs to use scmi_tlm_batch as a container structure instead of custom structs - add support for 0-items query pattern to all _LIST IOCTLs v7 --> v8 - Fixes for some Sashiko issues on v7 - refactor batch handling into common batch init/fini routines - use array_size to check for overflows - explain cleanup logic of eventfd_ctx (xa_for_each) - add a mutex to protect events_xa - introduce per-IOCTL check on batch upper bounds v6 --> v7 - fixed module_init errors paths - introduced a check on FMODE to filter-out unprivleged writers - added UUID list IOCTL - added UUID/SID/OFFS tp GET/SET config IOCTLs - added EVENT subscribe IOCTL - modified BATCH READ with per-DE status - added BATCHed mode for DE_CFG_SET/GET - fix setting properly discrete interval flag - new ABI feats flags\ - new SCMI feats flags - new reserved checks v5 --> v6 - drop hunam readable .read capabilities on chardev - fill-up the new UAPI de_implementation version u8 array - added compat_ioctl support - fixed initialization of flags in scmi_tlm_to_uapi_base_info - fix grp_id bounds check in scmi_tlm_grp_info_get_ioctl - fix booleans - fix SHMTI get list to loop on input params - reworked bounds checks into: - scmi_tlm_intervals_get_ioctl - scmi_tlm_des_list_get_ioctl - scmi_tlm_grp_list_get_ioctl - scmi_tlm_grp_desc_get_ioctl - renamed some READ ioctl to be consistent - reject oversized mmap requests - support new GET_ABI_INFO IOCTL - support RESET IOCTL - add a missing READ_ONCE() to rinfo get - avoid gaps in the anon fds returned array needed by shmti mmap - adding missing cdev_del on remove --- drivers/firmware/arm_scmi/Kconfig | 10 + drivers/firmware/arm_scmi/Makefile | 1 + .../firmware/arm_scmi/scmi_system_telemetry.c | 1720 +++++++++++++++++ 3 files changed, 1731 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 f506c4d1d96a..e7d8dd9fa907 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -113,4 +113,14 @@ config ARM_SCMI_POWER_CONTROL called scmi_power_control. Note this may be needed early in boot to catch early shutdown/reboot SCMI requests. =20 +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) +=3D scmi-core.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) +=3D scmi-module.o =20 obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) +=3D scmi_power_control.o +obj-$(CONFIG_ARM_SCMI_SYSTEM_TELEMETRY) +=3D scmi_system_telemetry.o diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/fi= rmware/arm_scmi/scmi_system_telemetry.c new file mode 100644 index 000000000000..729a7f909139 --- /dev/null +++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c @@ -0,0 +1,1720 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SCMI - System Telemetry Driver + * + * Copyright (C) 2026 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SCMI_TLM_DRIVER "scmi-telemetry-driver" +#define SCMI_TLM_NUM_DEVS 256 + +#define MAX_BULK_LINE_CHAR_LENGTH 64 + +/* + * Break if inadvertently struct scmi_telemetry_de_sample deviates from the + * UAPI counterpart struct scmi_tlm_de_sample: this allows more performant + * single DE reads. + */ +#define SCMI_DE_SAMPLE_BUILD_SANITY_CHECK \ +({ \ + BUILD_BUG_ON_MSG(sizeof(struct scmi_telemetry_de_sample) !=3D \ + sizeof(struct scmi_tlm_de_sample), \ + "UAPI DE sample mismatch !"); \ + ASSERT_STRUCT_OFFSET(struct scmi_telemetry_de_sample, id, \ + offsetof(struct scmi_tlm_de_sample, id)); \ + ASSERT_STRUCT_OFFSET(struct scmi_telemetry_de_sample, pad, \ + offsetof(struct scmi_tlm_de_sample, pad)); \ + ASSERT_STRUCT_OFFSET(struct scmi_telemetry_de_sample, tstamp, \ + offsetof(struct scmi_tlm_de_sample, tstamp));\ + ASSERT_STRUCT_OFFSET(struct scmi_telemetry_de_sample, val, \ + offsetof(struct scmi_tlm_de_sample, val)); \ +}) + +static dev_t stlm_devt; + +/** + * struct scmi_tlm_setup - Telemetry setup descriptor + * @sdev: A reference to the related SCMI device + * @ph: A reference to the protocol handle to be used with the ops + * @ops: A reference to the protocol ops + * @rinfo: A reference to the resource info descriptor + */ +struct scmi_tlm_setup { + struct scmi_device *sdev; + struct scmi_protocol_handle *ph; + const struct scmi_telemetry_proto_ops *ops; + const struct scmi_telemetry_res_info *rinfo; +}; + +/** + * struct scmi_tlm_instance - Telemetry instance descriptor + * @id: Progressive number identifying this probed instance. + * @devt: The dev_t descriptor attached to this instance. + * @cdev: An embedded CDEV structure. + * @node: A node to link this in the list of all instances. + * @tsp: A reference to the telemetry instance setup + * @info: A reference to this instance SCMI Telemetry info data. + * @sessions_xa: An XArray holding sessions descriptors, indexed by per-pr= ocess + * keys (@filp), that in turn contain the Telemetry events + * currently subscribed by this instance of the driver. + * @sessions_mtx: A mutex to protect sessions_xa + * @dead_mtx: A mutex to protect @dead + * @dead: A boolean to indicate when the device has been unboud + * @ref: A kref to track this structure lifecycle + */ +struct scmi_tlm_instance { + unsigned int id; + dev_t devt; + struct cdev cdev; + struct list_head node; + struct scmi_tlm_setup *tsp; + const struct scmi_telemetry_info *info; +#define SCMI_TLM_EVT_XA_MIN 1 +#define scmi_evt_xa_limit XA_LIMIT(SCMI_TLM_EVT_XA_MIN, UINT_MAX) + struct xarray sessions_xa; + /* Protect sessions_xa */ + struct mutex sessions_mtx; + /* Protect dead */ + struct mutex dead_mtx; + bool dead; + struct kref ref; +}; + +#define cdev_to_instance(c) container_of(c, struct scmi_tlm_instance, cdev) +#define kref_to_instance(r) container_of(r, struct scmi_tlm_instance, ref) + +struct scmi_tlm_shmti_ctx { + struct scmi_tlm_instance *ti; + struct scmi_telemetry_shmti_info *shmti; + struct kref ref; +}; + +#define to_shmti_ctx(c) container_of(c, struct scmi_tlm_shmti_ctx, ref) + +struct scmi_tlm_registered_event { + enum scmi_telemetry_event type; + struct eventfd_ctx *ctx; +}; + +struct scmi_tlm_events_session { + struct xarray events_xa; + /* Protect events_xa */ + struct mutex events_mtx; +}; + +static void scmi_tlm_instance_release(struct kref *ref); + +static inline void scmi_tlm_to_uapi_abi_info(struct scmi_tlm_abi_info *out, + const struct scmi_telemetry_info *in) +{ + out->size =3D sizeof(*out); + out->abi_version =3D SCMI_TLM_CURRENT_ABI_VERSION; + out->abi_features =3D SCMI_TLM_ABI_FEAT_RESET | SCMI_TLM_ABI_FEAT_EVENT | + SCMI_TLM_ABI_FEAT_UUID_LIST | SCMI_TLM_ABI_FEAT_BATCH_STATE | + SCMI_TLM_ABI_FEAT_BATCHED_CFG | SCMI_TLM_ABI_FEAT_DE_TRACKING; + export_uuid(out->primary_de_impl_version, &in->base.primary_revision); + out->num_des =3D in->base.num_des; + out->num_groups =3D in->base.num_groups; + out->num_intervals =3D in->base.num_intervals; + out->num_shmtis =3D in->base.num_shmtis; + out->features =3D in->reset_support ? SCMI_TLM_SCMI_SUPPORT_RESET : 0; + out->features |=3D in->single_read_support ? + SCMI_TLM_SCMI_SUPPORT_SINGLE_SAMPLE : 0; + out->features |=3D in->per_group_config_support ? + SCMI_TLM_SCMI_SUPPORT_GROUP_CONFIG : 0; + out->features |=3D in->continuos_update_support ? + SCMI_TLM_SCMI_SUPPORT_UPDATE_NOTIFICATION : 0; +} + +static inline void +scmi_tlm_to_uapi_intervals(struct scmi_tlm_intervals *out, + struct scmi_telemetry_intervals *in, + struct scmi_tlm_update_interval *out_interv) +{ + unsigned int *in_interv =3D in->update_intervals; + + out->flags |=3D in->discrete ? SCMI_TLM_INTERV_DISCRETE : 0; + for (int i =3D 0; i < out->num_intervals; i++) { + out_interv[i].secs =3D + SCMI_TLM_GET_UPDATE_INTERVAL_SECS(in_interv[i]); + out_interv[i].exp =3D + SCMI_TLM_GET_UPDATE_INTERVAL_EXP(in_interv[i]); + } +} + +static inline void scmi_tlm_to_uapi_de_info(struct scmi_tlm_de_info *out, + struct scmi_telemetry_de_info *in) +{ + out->id =3D in->id; + out->grp_id =3D in->grp_id; + out->data_sz =3D in->data_sz; + out->type =3D in->type; + out->unit =3D in->unit; + out->unit_exp =3D in->unit_exp; + out->ts_rate =3D in->ts_rate; + out->instance_id =3D in->instance_id; + out->compo_instance_id =3D in->compo_instance_id; + out->compo_type =3D in->compo_type; + out->persistent =3D !!in->persistent; + out->flags =3D in->grp_id !=3D SCMI_TLM_GRP_INVALID ? + SCMI_TLM_DEINFO_GROUP : 0; + out->pad[0] =3D 0; + out->pad[1] =3D 0; + out->pad2 =3D 0; + memcpy(out->name, in->name, 16); +} + +static inline void +scmi_tlm_to_uapi_des_items(unsigned int num_des, + const struct scmi_telemetry_res_info *in, + struct scmi_tlm_de_info *des) +{ + for (int i =3D 0; i < num_des; i++) + scmi_tlm_to_uapi_de_info(&des[i], &in->dei_store[i]); +} + +static inline void scmi_tlm_to_uapi_grp_info(struct scmi_tlm_grp_info *out, + struct scmi_telemetry_grp_info *in) +{ + out->grp_id =3D in->grp_id; + out->num_des =3D in->num_des; + out->num_intervals =3D in->num_intervals; + out->pad =3D 0; +} + +static inline void +scmi_tlm_to_uapi_grps_list(unsigned int num_groups, + const struct scmi_telemetry_res_info *in, + struct scmi_tlm_grp_info *ginfo) +{ + struct scmi_tlm_grp_info *out_ginfo =3D ginfo; + + for (int i =3D 0; i < num_groups; i++) + scmi_tlm_to_uapi_grp_info(&out_ginfo[i], &in->grps_store[i]); +} + +static inline void +scmi_tlm_to_uapi_composing_des(struct scmi_tlm_grp_desc *out, + const struct scmi_telemetry_res_info *in, + u32 *composing_des) +{ + struct scmi_telemetry_group *grp =3D &in->grps[out->grp_id]; + + for (int i =3D 0; i < out->num_des; i++) + composing_des[i] =3D in->des[grp->des[i]]->info->id; +} + +/** + * scmi_telemetry_res_info_get - Resources info getter + * @tsp: A reference to the telemetry instance setup + * + * On first call this helper takes care to retrieve and cache all the reso= urces + * descriptor from the platform, then, on the following invocations it will + * always return the cached value. + */ +static inline const struct scmi_telemetry_res_info * +scmi_telemetry_res_info_get(struct scmi_tlm_setup *tsp) +{ + const struct scmi_telemetry_res_info *rinfo; + + if (READ_ONCE(tsp->rinfo)) + return tsp->rinfo; + + rinfo =3D tsp->ops->res_get(tsp->ph); + /* Cache the retrieved resource info value */ + smp_store_mb(tsp->rinfo, rinfo); + + return rinfo; +} + +static long +scmi_tlm_abi_info_get_ioctl(const struct scmi_tlm_instance *ti, unsigned l= ong arg) +{ + struct scmi_tlm_abi_info base =3D {}; + void __user *uptr =3D (void __user *)arg; + bool ignored_trailing; + u32 usize; + int err; + + if (get_user(usize, (u32 __user *)arg)) + return -EFAULT; + + if (usize < offsetofend(struct scmi_tlm_abi_info, reserved)) + return -EINVAL; + + if (copy_struct_from_user(&base, sizeof(base), uptr, usize)) + return -EFAULT; + + if (base.reserved) + return -EINVAL; + + scmi_tlm_to_uapi_abi_info(&base, ti->info); + err =3D copy_struct_to_user(uptr, usize, &base, sizeof(base), + &ignored_trailing); + if (err) + return err; + + if (ignored_trailing) + return -EMSGSIZE; + + return 0; +} + +static long +scmi_tlm_config_get_ioctl(const struct scmi_tlm_instance *ti, unsigned lon= g arg) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_config cfg; + + if (copy_from_user(&cfg, uptr, sizeof(cfg))) + return -EFAULT; + + if (cfg.pad || cfg.reserved || cfg.flags & ~SCMI_TLM_CONFIG_FLAGS) + return -EINVAL; + + if (!(SCMI_TLM_CONFIG_IS_GROUP(cfg.flags))) { + unsigned int active =3D ti->info->active_update_interval; + + cfg.enable =3D !!ti->info->enabled; + cfg.active.secs =3D SCMI_TLM_GET_UPDATE_INTERVAL_SECS(active); + cfg.active.exp =3D SCMI_TLM_GET_UPDATE_INTERVAL_EXP(active); + } else { + const struct scmi_telemetry_res_info *rinfo; + struct scmi_telemetry_group *grp; + unsigned int active; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + if (cfg.grp_id >=3D rinfo->num_groups) + return -EINVAL; + + grp =3D &rinfo->grps[cfg.grp_id]; + active =3D grp->active_update_interval; + + cfg.enable =3D grp->enabled; + cfg.t_enable =3D grp->tstamp_enabled; + cfg.active.secs =3D SCMI_TLM_GET_UPDATE_INTERVAL_SECS(active); + cfg.active.exp =3D SCMI_TLM_GET_UPDATE_INTERVAL_EXP(active); + } + + if (copy_to_user(uptr, &cfg, sizeof(cfg))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_config_set_ioctl(const struct scmi_tlm_instance *ti, unsigned lon= g arg) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_setup *tsp =3D ti->tsp; + struct scmi_tlm_config cfg; + unsigned int active; + bool ena, t_ena; + + if (copy_from_user(&cfg, uptr, sizeof(cfg))) + return -EFAULT; + + if (cfg.pad || cfg.reserved || cfg.flags & ~SCMI_TLM_CONFIG_FLAGS) + return -EINVAL; + + ena =3D !!cfg.enable; + t_ena =3D !!cfg.t_enable; + if (!SCMI_TLM_CONFIG_IS_GROUP(cfg.flags)) { + cfg.grp_id =3D SCMI_TLM_GRP_INVALID; + } else { + int ret; + + ret =3D tsp->ops->state_set(tsp->ph, true, cfg.grp_id, &ena, &t_ena, + NULL, NULL, NULL); + if (ret) + return ret; + } + + active =3D SCMI_TLM_BUILD_UPDATE_INTERVAL(cfg.active.secs, cfg.active.exp= ); + + return tsp->ops->collection_configure(tsp->ph, cfg.grp_id, &ena, &active,= NULL); +} + +static long +scmi_tlm_intervals_get_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + struct scmi_telemetry_intervals *tlm_ivs; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_intervals ivs; + size_t ivs_intrv_sz; + + if (copy_from_user(&ivs, uptr, sizeof(ivs))) + return -EFAULT; + + if (ivs.pad[0] || ivs.pad[1] || ivs.pad[2] || ivs.pad2 || + ivs.reserved || ivs.flags & ~SCMI_TLM_INTERV_FLAGS) + return -EINVAL; + + if (!SCMI_TLM_INTERV_IS_GROUP(ivs.flags)) { + tlm_ivs =3D ti->info->intervals; + } else { + const struct scmi_telemetry_res_info *rinfo; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + if (ivs.grp_id >=3D rinfo->num_groups) + return -EINVAL; + + tlm_ivs =3D rinfo->grps[ivs.grp_id].intervals; + } + + /* Return max interval on zero-query */ + if (!ivs.num_intervals) { + ivs.num_intervals =3D tlm_ivs->num_intervals; + + if (copy_to_user(uptr, &ivs, sizeof(ivs))) + return -EFAULT; + + return 0; + } + + /* Reject only oversized output buffers */ + if (ivs.num_intervals > tlm_ivs->num_intervals) + return -ENOSPC; + + ivs_intrv_sz =3D array_size(ivs.num_intervals, + sizeof(struct scmi_tlm_update_interval)); + if (ivs_intrv_sz =3D=3D SIZE_MAX) + return -EOVERFLOW; + + /* A local scratch buffer... */ + struct scmi_tlm_update_interval *ivs_intrv __free(kvfree) =3D + kvzalloc(ivs_intrv_sz, GFP_KERNEL); + if (!ivs_intrv) + return -ENOMEM; + + if (copy_from_user(ivs_intrv, u64_to_user_ptr(ivs.intervals), + ivs_intrv_sz)) + return -EFAULT; + + /* Passing the array as a param avoids dancing with uptr->intervals */ + scmi_tlm_to_uapi_intervals(&ivs, tlm_ivs, ivs_intrv); + if (copy_to_user(u64_to_user_ptr(ivs.intervals), + (void *)ivs_intrv, ivs_intrv_sz)) + return -EFAULT; + + if (copy_to_user(uptr, &ivs, sizeof(ivs))) + return -EFAULT; + + return 0; +} + +static int +scmi_tlm_batch_initialize(void __user *uptr, struct scmi_tlm_batch *batch, + size_t item_sz, unsigned int max_items, + void **out_items, size_t *out_batch_sz, + int **out_states, size_t *out_states_sz) +{ + size_t batch_sz; + + if (copy_from_user(batch, uptr, sizeof(*batch))) + return -EFAULT; + + if (batch->reserved || batch->item_sz !=3D item_sz || + batch->num_items > max_items) + return -EINVAL; + + /* Sanity check on user invocation */ + if (!out_items || !out_batch_sz) + return -EINVAL; + + /* Was a size query ? */ + if (batch->num_items =3D=3D 0) { + *out_items =3D NULL; + *out_batch_sz =3D 0; + return 0; + } + + batch_sz =3D array_size(batch->num_items, batch->item_sz); + if (batch_sz =3D=3D SIZE_MAX) + return -EOVERFLOW; + + void *items __free(kvfree) =3D kvzalloc(batch_sz, GFP_KERNEL); + if (!items) + return -ENOMEM; + + /* Read all the requested configs */ + if (copy_from_user(items, u64_to_user_ptr(batch->items), batch_sz)) + return -EFAULT; + + /* Is per-item status required ? */ + if (batch->states) { + size_t states_sz; + + states_sz =3D array_size(batch->num_items, sizeof(int)); + if (states_sz =3D=3D SIZE_MAX) + return -EOVERFLOW; + + int *states_arr __free(kvfree) =3D kvzalloc(states_sz, GFP_KERNEL); + if (!states_arr) + return -ENOMEM; + + if (out_states) + *out_states =3D no_free_ptr(states_arr); + if (out_states_sz) + *out_states_sz =3D states_sz; + } else { + if (out_states) + *out_states =3D NULL; + if (out_states_sz) + *out_states_sz =3D 0; + } + + *out_items =3D no_free_ptr(items); + *out_batch_sz =3D batch_sz; + + return 0; +} + +static int +scmi_tlm_batch_finalize(void __user *uptr, struct scmi_tlm_batch *batch, + void *items, int *states, size_t batch_sz, + size_t states_sz) +{ + if (batch_sz) { + if (copy_to_user(u64_to_user_ptr(batch->items), items, batch_sz)) + return -EFAULT; + + if (batch->states) { + if (copy_to_user(u64_to_user_ptr(batch->states), + states, states_sz)) + return -EFAULT; + } + } + + if (copy_to_user(uptr, batch, sizeof(*batch))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_all_de_config_set_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_de_config tcfg =3D {}; + bool ena, t_ena; + int ret =3D 0; + + if (copy_from_user(&tcfg, uptr, sizeof(tcfg))) + return -EFAULT; + + if (tcfg.reserved) + return -EINVAL; + + ena =3D !!tcfg.enable; + t_ena =3D !!tcfg.t_enable; + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + for (int i =3D 0; i < rinfo->num_des; i++) { + int err; + + err =3D ti->tsp->ops->state_set(ti->tsp->ph, false, + rinfo->des[i]->info->id, + &ena, &t_ena, NULL, NULL, NULL); + if (err) { + /* Carry on BUT report last error */ + ret =3D err; + continue; + } + } + + return ret; +} + +static long +scmi_tlm_all_de_config_get_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_de_config tcfg =3D {}; + bool ena, t_ena; + int ret; + + if (copy_from_user(&tcfg, uptr, sizeof(tcfg))) + return -EFAULT; + + if (tcfg.reserved) + return -EINVAL; + + ena =3D !!tcfg.enable; + t_ena =3D !!tcfg.t_enable; + ret =3D ti->tsp->ops->state_get(ti->tsp->ph, NULL, &ena, &t_ena, + NULL, NULL, NULL); + if (ret) + return ret; + + tcfg.enable =3D !!ena; + tcfg.t_enable =3D !!t_ena; + if (copy_to_user(uptr, &tcfg, sizeof(tcfg))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_de_config_set_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + struct scmi_tlm_de_config *tcfg __free(kvfree) =3D NULL; + int ret, *states __free(kvfree) =3D NULL; + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz, states_sz; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*tcfg), + rinfo->num_des, (void **)&tcfg, &batch_sz, + &states, &states_sz); + if (ret) + return ret; + + for (int i =3D 0; i < batch.num_items; i++) { + unsigned int sid, offset; + bool ena, t_ena; + uuid_t uuid; + int ret; + + ena =3D !!tcfg[i].enable; + t_ena =3D !!tcfg[i].t_enable; + + ret =3D ti->tsp->ops->state_set(ti->tsp->ph, false, + tcfg[i].id, &ena, &t_ena, + &sid, &offset, &uuid); + if (ret) { + if (!states) + return ret; + + states[i] =3D ret; + continue; + } + + tcfg[i].sid =3D sid; + tcfg[i].offset =3D offset; + export_uuid(tcfg[i].uuid, &uuid); + } + + /* Return max batch size on zero query */ + if (!batch.num_items) + batch.num_items =3D rinfo->num_des; + + return scmi_tlm_batch_finalize(uptr, &batch, tcfg, states, batch_sz, + states_sz); +} + +static long +scmi_tlm_de_config_get_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + struct scmi_tlm_de_config *tcfg __free(kvfree) =3D NULL; + int ret, *states __free(kvfree) =3D NULL; + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz, states_sz; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*tcfg), + rinfo->num_des, (void **)&tcfg, + &batch_sz, &states, &states_sz); + if (ret) + return ret; + + for (int i =3D 0; i < batch.num_items; i++) { + unsigned int sid =3D SCMI_TLM_SHMTI_ID_INVALID, offset =3D 0; + bool ena, t_ena; + uuid_t uuid =3D {}; + + ena =3D !!tcfg[i].enable; + t_ena =3D !!tcfg[i].t_enable; + ret =3D ti->tsp->ops->state_get(ti->tsp->ph, &tcfg[i].id, + &ena, &t_ena, &sid, &offset, &uuid); + + if (ret) { + if (!states) + return ret; + + states[i] =3D ret; + continue; + } + + tcfg[i].enable =3D ena; + tcfg[i].t_enable =3D t_ena; + tcfg[i].sid =3D sid; + tcfg[i].offset =3D offset; + export_uuid(tcfg[i].uuid, &uuid); + } + + /* Return max batch size on zero query */ + if (!batch.num_items) + batch.num_items =3D rinfo->num_des; + + return scmi_tlm_batch_finalize(uptr, &batch, tcfg, states, batch_sz, + states_sz); +} + +static long +scmi_tlm_de_info_get_ioctl(const struct scmi_tlm_instance *ti, unsigned lo= ng arg) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_setup *tsp =3D ti->tsp; + const struct scmi_telemetry_de *de; + struct scmi_tlm_de_info dei; + + if (copy_from_user(&dei, uptr, sizeof(dei))) + return -EFAULT; + + /* Flags does NOT make sense in the input param */ + if (dei.pad[0] || dei.pad[1] || dei.flags || dei.reserved) + return -EINVAL; + + de =3D tsp->ops->de_lookup(tsp->ph, dei.id); + if (!de) + return -EINVAL; + + scmi_tlm_to_uapi_de_info(&dei, de->info); + if (copy_to_user(uptr, &dei, sizeof(dei))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_des_list_get_ioctl(const struct scmi_tlm_instance *ti, unsigned l= ong arg) +{ + struct scmi_tlm_de_info *dei __free(kvfree) =3D NULL; + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz; + int ret; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*dei), + rinfo->num_des, (void **)&dei, &batch_sz, + NULL, NULL); + if (ret) + return ret; + + /* Passing the array as a param avoids dancing with uptr->intervals */ + if (batch.num_items) + scmi_tlm_to_uapi_des_items(batch.num_items, rinfo, dei); + else + batch.num_items =3D rinfo->num_des; + + return scmi_tlm_batch_finalize(uptr, &batch, dei, NULL, batch_sz, 0); +} + +static long +scmi_tlm_de_value_get_ioctl(const struct scmi_tlm_instance *ti, unsigned l= ong arg) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_setup *tsp =3D ti->tsp; + struct scmi_tlm_de_sample sample; + int ret; + + SCMI_DE_SAMPLE_BUILD_SANITY_CHECK; + + if (copy_from_user(&sample, uptr, sizeof(sample))) + return -EFAULT; + + if (sample.pad) + return -EINVAL; + + ret =3D 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_instance *ti, unsigned l= ong arg) +{ + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_grp_info ginfo; + + if (copy_from_user(&ginfo, uptr, sizeof(ginfo))) + return -EFAULT; + + if (ginfo.pad || ginfo.reserved) + return -EINVAL; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + if (ginfo.grp_id >=3D rinfo->num_groups) + return -EINVAL; + + scmi_tlm_to_uapi_grp_info(&ginfo, &rinfo->grps_store[ginfo.grp_id]); + if (copy_to_user(uptr, &ginfo, sizeof(ginfo))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_grp_desc_get_ioctl(const struct scmi_tlm_instance *ti, unsigned l= ong arg) +{ + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_grp_desc gdesc; + size_t composing_sz; + + if (copy_from_user(&gdesc, uptr, sizeof(gdesc))) + return -EFAULT; + + if (gdesc.reserved) + return -EINVAL; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + if (gdesc.grp_id >=3D rinfo->num_groups) + return -EINVAL; + + /* Reject only oversized output buffers */ + if (gdesc.num_des > rinfo->grps_store[gdesc.grp_id].num_des) + return -ENOSPC; + + composing_sz =3D array_size(gdesc.num_des, sizeof(u32)); + if (composing_sz =3D=3D SIZE_MAX) + return -EOVERFLOW; + + /* A local scratch buffer... */ + u32 *composing_des __free(kvfree) =3D kvzalloc(composing_sz, GFP_KERNEL); + if (!composing_des) + return -ENOMEM; + + if (copy_from_user(composing_des, u64_to_user_ptr(gdesc.composing_des), + composing_sz)) + return -EFAULT; + + /* Passing the array as a param avoids dancing with uptr->intervals */ + scmi_tlm_to_uapi_composing_des(&gdesc, rinfo, composing_des); + if (copy_to_user(u64_to_user_ptr(gdesc.composing_des), composing_des, + composing_sz)) + return -EFAULT; + + if (copy_to_user(uptr, &gdesc, sizeof(gdesc))) + return -EFAULT; + + return 0; +} + +static long +scmi_tlm_grps_list_get_ioctl(const struct scmi_tlm_instance *ti, unsigned = long arg) +{ + struct scmi_tlm_grp_info *ginfo __free(kvfree) =3D NULL; + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz; + int ret; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*ginfo), + rinfo->num_groups, (void **)&ginfo, + &batch_sz, NULL, NULL); + if (ret) + return ret; + + /* Passing the array as a param avoids dancing with uptr->intervals */ + if (batch.num_items) + scmi_tlm_to_uapi_grps_list(batch.num_items, rinfo, ginfo); + else + batch.num_items =3D rinfo->num_groups; + + return scmi_tlm_batch_finalize(uptr, &batch, ginfo, NULL, batch_sz, 0); +} + +static long scmi_tlm_des_read_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg, bool single) +{ + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_setup *tsp =3D ti->tsp; + struct scmi_tlm_data_read bulk; + unsigned int grp_id; + int ret; + + if (copy_from_user(&bulk, uptr, sizeof(bulk))) + return -EFAULT; + + if (bulk.pad[0] || bulk.pad[1] || bulk.pad[2] || bulk.pad2 || + bulk.flags & ~SCMI_TLM_READ_FLAGS) + return -EINVAL; + + /* Check that a reasonably sized buffer has been requested */ + rinfo =3D scmi_telemetry_res_info_get(tsp); + if (bulk.num_samples > rinfo->num_des) + return -E2BIG; + + if (!SCMI_TLM_READ_IS_GROUP(bulk.flags)) { + grp_id =3D SCMI_TLM_GRP_INVALID; + } else { + if (bulk.grp_id >=3D rinfo->num_groups) + return -EINVAL; + + grp_id =3D bulk.grp_id; + } + + /* Return max samples on zero-query */ + if (!bulk.num_samples) { + if (!SCMI_TLM_READ_IS_GROUP(bulk.flags)) + bulk.num_samples =3D rinfo->num_des; + else + bulk.num_samples =3D rinfo->grps[grp_id].info->num_des; + + if (copy_to_user(uptr, &bulk, sizeof(bulk))) + return -EFAULT; + + return 0; + } + + struct scmi_telemetry_de_sample *samples __free(kvfree) =3D + kvcalloc(bulk.num_samples, sizeof(*samples), GFP_KERNEL); + if (!samples) + return -ENOMEM; + + if (!single) + ret =3D tsp->ops->des_bulk_read(tsp->ph, grp_id, + &bulk.num_samples, samples); + else + ret =3D tsp->ops->des_sample_get(tsp->ph, grp_id, + &bulk.num_samples, samples); + if (ret) + return ret; + + if (copy_to_user(u64_to_user_ptr(bulk.samples), samples, + bulk.num_samples * sizeof(*samples))) + return -EFAULT; + + if (copy_to_user(uptr, &bulk, sizeof(bulk))) + return -EFAULT; + + return 0; +} + +static long scmi_tlm_des_batch_read_ioctl(const struct scmi_tlm_instance *= ti, + unsigned long arg) +{ + struct scmi_telemetry_de_sample *samples __free(kvfree) =3D NULL; + int ret, *states __free(kvfree) =3D NULL; + const struct scmi_telemetry_res_info *rinfo; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz, states_sz; + + rinfo =3D scmi_telemetry_res_info_get(ti->tsp); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*samples), + rinfo->num_des, (void **)&samples, + &batch_sz, &states, &states_sz); + if (ret) + return ret; + + for (int i =3D 0; i < batch.num_items; i++) { + int ret; + + ret =3D ti->tsp->ops->de_data_read(ti->tsp->ph, &samples[i]); + if (ret) { + if (!states) + return ret; + + states[i] =3D ret; + continue; + } + } + + /* Return max batch size on zero query */ + if (!batch.num_items) + batch.num_items =3D rinfo->num_des; + + return scmi_tlm_batch_finalize(uptr, &batch, samples, states, batch_sz, + states_sz); +} + +static struct scmi_tlm_shmti_ctx * +scmi_tlm_shmti_ctx_alloc(struct scmi_tlm_instance *ti, + struct scmi_telemetry_shmti_info *shmti) +{ + struct scmi_tlm_shmti_ctx *ctx; + + ctx =3D kzalloc_obj(*ctx); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->shmti =3D shmti; + kref_init(&ctx->ref); + + /* Get hold of the Telemetry instance when mmapping */ + kref_get(&ti->ref); + ctx->ti =3D ti; + + return ctx; +} + +static void scmi_tlm_shmti_ctx_release(struct kref *kref) +{ + struct scmi_tlm_shmti_ctx *ctx =3D to_shmti_ctx(kref); + + kref_put(&ctx->ti->ref, scmi_tlm_instance_release); + + kfree(ctx); +} + +static struct scmi_tlm_shmti_ctx * +scmi_tlm_shmti_ctx_get(struct scmi_tlm_shmti_ctx *ctx) +{ + kref_get(&ctx->ref); + + return ctx; +} + +static void scmi_tlm_shmti_ctx_put(struct scmi_tlm_shmti_ctx *ctx) +{ + kref_put(&ctx->ref, scmi_tlm_shmti_ctx_release); +} + +static void scmi_tlm_shmti_vma_open(struct vm_area_struct *vma) +{ + scmi_tlm_shmti_ctx_get(vma->vm_private_data); +} + +static void scmi_tlm_shmti_vma_close(struct vm_area_struct *vma) +{ + scmi_tlm_shmti_ctx_put(vma->vm_private_data); +} + +static const struct vm_operations_struct scmi_tlm_shmti_vm_ops =3D { + .open =3D scmi_tlm_shmti_vma_open, + .close =3D scmi_tlm_shmti_vma_close, +}; + +static int scmi_tlm_shmti_mmap(struct file *filp, struct vm_area_struct *v= ma) +{ + struct scmi_tlm_shmti_ctx *ctx =3D filp->private_data; + unsigned long npages, expect, needed, req =3D vma->vm_end - vma->vm_start; + phys_addr_t base; + size_t map_sz; + int ret; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (req < ctx->shmti->len) + return -EINVAL; + + /* Align & reject oversized requests */ + base =3D ctx->shmti->phys & PAGE_MASK; + needed =3D ctx->shmti->len + ctx->shmti->offset; + expect =3D DIV_ROUND_UP(needed, PAGE_SIZE); + npages =3D req >> PAGE_SHIFT; + if (npages > expect) + return -EINVAL; + + map_sz =3D PAGE_ALIGN(needed); + vma->vm_private_data =3D scmi_tlm_shmti_ctx_get(ctx); + vma->vm_ops =3D &scmi_tlm_shmti_vm_ops; + /* Set appropriate caching attributes */ + vma->vm_page_prot =3D pgprot_noncached(vma->vm_page_prot); + + /* Map physical address range into user space */ + ret =3D vm_iomap_memory(vma, base, map_sz); + if (ret) { + scmi_tlm_shmti_ctx_put(ctx); + return ret; + } + + return 0; +} + +static int scmi_tlm_shmti_release(struct inode *ino, struct file *filp) +{ + scmi_tlm_shmti_ctx_put(filp->private_data); + + return 0; +} + +static const struct file_operations scmi_tlm_shmti_fops =3D { + .owner =3D THIS_MODULE, + .mmap =3D scmi_tlm_shmti_mmap, + .release =3D scmi_tlm_shmti_release, +}; + +static void +scmi_tlm_allocate_anon_fds(struct scmi_tlm_instance *ti, unsigned int *num= _shmtis, + struct scmi_tlm_shmti_info *shinfo, + struct file **filps) +{ + const struct scmi_telemetry_info *info =3D ti->info; + struct device *dev =3D &ti->tsp->sdev->dev; + int idx =3D 0; + + for (int i =3D 0; i < *num_shmtis; i++) { + struct scmi_telemetry_shmti_info *shmti =3D info->shmtis[i]; + struct scmi_tlm_shmti_ctx *ctx; + struct file *filp; + int fd; + + fd =3D get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + dev_err(dev, "Failed GET_UNUSED_FD for SID: %u\n", + shmti->sid); + continue; + } + + ctx =3D scmi_tlm_shmti_ctx_alloc(ti, shmti); + if (IS_ERR(ctx)) { + dev_err(dev, "Failed CTX_ALLOC for SID: %u\n", shmti->sid); + put_unused_fd(fd); + continue; + } + + filp =3D anon_inode_getfile(SCMI_TLM_DRIVER, &scmi_tlm_shmti_fops, + ctx, O_RDONLY | O_CLOEXEC); + if (IS_ERR(filp)) { + scmi_tlm_shmti_ctx_put(ctx); + put_unused_fd(fd); + dev_err(dev, "Failed ANON GET_FILE for SID: %u\n", + shmti->sid); + continue; + } + + shinfo[idx].sid =3D shmti->sid; + shinfo[idx].fd =3D fd; + shinfo[idx].len =3D shmti->len; + shinfo[idx].offset =3D shmti->offset; + + filps[idx++] =3D filp; + } + + /* Return effective initialized items */ + *num_shmtis =3D idx; +} + +static inline void +scmi_tlm_anon_fds_free(int num_shmtis, struct scmi_tlm_shmti_info *shinfo, + struct file **filps) +{ + for (int i =3D 0; i < num_shmtis; i++) { + put_unused_fd(shinfo[i].fd); + fput(filps[i]); + } +} + +static inline void +scmi_tlm_anon_fds_install(int num_shmtis, struct scmi_tlm_shmti_info *shin= fo, + struct file **filps) +{ + for (int i =3D 0; i < num_shmtis; i++) + fd_install(shinfo[i].fd, filps[i]); +} + +static long +scmi_tlm_shmtis_list_get_ioctl(struct scmi_tlm_instance *ti, unsigned long= arg) +{ + struct scmi_tlm_shmti_info *shinfo __free(kvfree) =3D NULL; + unsigned int max_shmtis =3D ti->info->base.num_shmtis; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + size_t batch_sz; + int ret; + + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*shinfo), max_shmt= is, + (void **)&shinfo, &batch_sz, NULL, NULL); + if (ret) + return ret; + + if (batch.num_items =3D=3D 0) { + batch.num_items =3D max_shmtis; + + return scmi_tlm_batch_finalize(uptr, &batch, shinfo, NULL, + batch_sz, 0); + } + + struct file **filps __free(kfree) =3D + kcalloc(batch.num_items, sizeof(*filps), GFP_KERNEL); + if (!filps) + return -ENOMEM; + + /* Passing the array as a param avoids dancing with uptr->items */ + scmi_tlm_allocate_anon_fds(ti, &batch.num_items, shinfo, filps); + ret =3D scmi_tlm_batch_finalize(uptr, &batch, shinfo, NULL, batch_sz, 0); + if (ret) { + scmi_tlm_anon_fds_free(batch.num_items, shinfo, filps); + return ret; + } + + scmi_tlm_anon_fds_install(batch.num_items, shinfo, filps); + + return 0; +} + +static long scmi_tlm_reset_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + return ti->tsp->ops->reset(ti->tsp->ph); +} + +static long +scmi_tlm_uuid_list_get_ioctl(const struct scmi_tlm_instance *ti, + unsigned long arg) +{ + struct scmi_tlm_uuid *uuids __free(kvfree) =3D NULL; + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_batch batch =3D {}; + unsigned int num_uuids; + uuid_t **cur_uuids; + size_t batch_sz; + int ret; + + /* + * Note that UUID array can ONLY grow and it has NOT a known upper + * bound, so it does not matter if by the time we grab the cur_uuids + * via rcu_dereference() the array has grown in size and we end up + * here reading a stale/incomplete set of UUIDs, we will simply + * read the new UUIDs next-time: this simplifies allocation. + */ + num_uuids =3D smp_load_acquire(&ti->info->num_uuids); + ret =3D scmi_tlm_batch_initialize(uptr, &batch, sizeof(*uuids), + num_uuids, (void **)&uuids, &batch_sz, + NULL, NULL); + if (ret) + return ret; + + rcu_read_lock(); + cur_uuids =3D rcu_dereference(ti->info->uuids); + for (int j =3D 0; j < batch.num_items; j++) + export_uuid(uuids[j].bytes, cur_uuids[j]); + rcu_read_unlock(); + + /* Return max batch size on zero query */ + if (!batch.num_items) + batch.num_items =3D num_uuids; + + return scmi_tlm_batch_finalize(uptr, &batch, uuids, NULL, batch_sz, 0); +} + +static int scmi_tlm_event_subscribe(struct scmi_tlm_instance *ti, + struct scmi_tlm_event *evt, void *key) +{ + struct scmi_tlm_events_session *session; + struct scmi_tlm_registered_event *revt; + struct eventfd_ctx *ctx; + u32 cookie; + int ret; + + scoped_guard(mutex, &ti->sessions_mtx) { + session =3D xa_load(&ti->sessions_xa, (unsigned long)key); + if (!session) { + /* + * Initialize a new session on first call. + * Once created successfully initialized sessions are + * freed only on final release. + */ + session =3D kzalloc_obj(*session); + if (!session) + return -ENOMEM; + + /* Initialize inner with proper flags to support xa_alloc */ + xa_init_flags(&session->events_xa, XA_FLAGS_ALLOC); + mutex_init(&session->events_mtx); + ret =3D xa_insert(&ti->sessions_xa, (unsigned long)key, + session, GFP_KERNEL); + if (ret) { + xa_destroy(&session->events_xa); + kfree(session); + return ret; + } + } + } + + ctx =3D eventfd_ctx_fdget(evt->efd); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + revt =3D kzalloc_obj(*revt); + if (!revt) { + eventfd_ctx_put(ctx); + return -ENOMEM; + } + + guard(mutex)(&session->events_mtx); + revt->ctx =3D ctx; + revt->type =3D evt->type; + ret =3D xa_alloc(&session->events_xa, &cookie, revt, + scmi_evt_xa_limit, GFP_KERNEL); + if (ret) { + eventfd_ctx_put(revt->ctx); + kfree(revt); + return ret; + } + + ret =3D ti->tsp->ops->event_subscribe(ti->tsp->ph, revt->type, revt->ctx); + if (ret) { + xa_erase(&session->events_xa, cookie); + eventfd_ctx_put(revt->ctx); + kfree(revt); + return ret; + } + + /* Return the allocated cookie */ + evt->cookie =3D cookie; + + /* Get hold of the instance */ + kref_get(&ti->ref); + + return 0; +} + +static int scmi_tlm_event_unsubscribe(struct scmi_tlm_instance *ti, + struct scmi_tlm_event *evt, void *key) +{ + struct scmi_tlm_events_session *session; + struct scmi_tlm_registered_event *revt; + int ret; + + if (!evt->cookie) + return -EINVAL; + + scoped_guard(mutex, &ti->sessions_mtx) { + session =3D xa_load(&ti->sessions_xa, (unsigned long)key); + if (!session) + return -ENODEV; + } + + guard(mutex)(&session->events_mtx); + revt =3D xa_load(&session->events_xa, evt->cookie); + if (!revt) + return -ENODEV; + + if (revt->type !=3D evt->type) + return -EINVAL; + + ret =3D ti->tsp->ops->event_unsubscribe(ti->tsp->ph, revt->type, revt->ct= x); + if (ret) + return ret; + + /* Clean up the registered event ONLY once successfully unsubscribed */ + xa_erase(&session->events_xa, evt->cookie); + eventfd_ctx_put(revt->ctx); + kfree(revt); + + kref_put(&ti->ref, scmi_tlm_instance_release); + + return 0; +} + +static long scmi_tlm_event_subscribe_ioctl(struct scmi_tlm_instance *ti, + unsigned long arg, void *session) +{ + void __user *uptr =3D (void __user *)arg; + struct scmi_tlm_event evt =3D {}; + int ret; + + if (copy_from_user(&evt, uptr, sizeof(evt))) + return -EFAULT; + + if (evt.pad || evt.reserved) + return -EINVAL; + + /* Reject unknown event types */ + if (evt.type > SCMI_TLM_EVT_LAST) + return -ENODEV; + + if (!evt.cookie) { + /* Register a per-session new event, returning new cookie */ + ret =3D scmi_tlm_event_subscribe(ti, &evt, session); + if (ret) + return ret; + + if (copy_to_user(uptr, &evt, sizeof(evt))) { + scmi_tlm_event_unsubscribe(ti, &evt, session); + return -EFAULT; + } + } else { + /* DE-Register if existent, put context */ + ret =3D scmi_tlm_event_unsubscribe(ti, &evt, session); + if (ret) + return ret; + } + + return 0; +} + +static long scmi_tlm_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct scmi_tlm_instance *ti =3D filp->private_data; + bool writable =3D filp->f_mode & FMODE_WRITE; + + guard(mutex)(&ti->dead_mtx); + if (ti->dead) + return -ENODEV; + + switch (cmd) { + case SCMI_TLM_GET_ABI_INFO: + return scmi_tlm_abi_info_get_ioctl(ti, arg); + case SCMI_TLM_GET_CFG: + return scmi_tlm_config_get_ioctl(ti, arg); + case SCMI_TLM_SET_CFG: + if (writable) + return scmi_tlm_config_set_ioctl(ti, arg); + break; + case SCMI_TLM_GET_INTRVS: + return scmi_tlm_intervals_get_ioctl(ti, arg); + case SCMI_TLM_GET_DE_CFG: + return scmi_tlm_de_config_get_ioctl(ti, arg); + case SCMI_TLM_SET_DE_CFG: + if (writable) + return scmi_tlm_de_config_set_ioctl(ti, arg); + break; + case SCMI_TLM_GET_DE_INFO: + return scmi_tlm_de_info_get_ioctl(ti, arg); + case SCMI_TLM_GET_DE_LIST: + return scmi_tlm_des_list_get_ioctl(ti, arg); + case SCMI_TLM_DE_READ: + return scmi_tlm_de_value_get_ioctl(ti, arg); + case SCMI_TLM_GET_ALL_CFG: + return scmi_tlm_all_de_config_get_ioctl(ti, arg); + case SCMI_TLM_SET_ALL_CFG: + if (writable) + return scmi_tlm_all_de_config_set_ioctl(ti, arg); + break; + case SCMI_TLM_GET_GRP_LIST: + return scmi_tlm_grps_list_get_ioctl(ti, arg); + case SCMI_TLM_GET_GRP_INFO: + return scmi_tlm_grp_info_get_ioctl(ti, arg); + case SCMI_TLM_GET_GRP_DESC: + return scmi_tlm_grp_desc_get_ioctl(ti, arg); + case SCMI_TLM_SINGLE_READ: + 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_BATCH_READ: + return scmi_tlm_des_batch_read_ioctl(ti, arg); + case SCMI_TLM_GET_SHMTI_LIST: + return scmi_tlm_shmtis_list_get_ioctl(ti, arg); + case SCMI_TLM_RESET: + if (writable) + return scmi_tlm_reset_ioctl(ti, arg); + break; + case SCMI_TLM_GET_UUID_LIST: + return scmi_tlm_uuid_list_get_ioctl(ti, arg); + case SCMI_TLM_EVENT_SUBSCRIBE: + return scmi_tlm_event_subscribe_ioctl(ti, arg, filp); + default: + return -ENOTTY; + } + + return -EPERM; +} + +static int scmi_tlm_open(struct inode *ino, struct file *filp) +{ + struct scmi_tlm_instance *ti =3D cdev_to_instance(ino->i_cdev); + + guard(mutex)(&ti->dead_mtx); + if (ti->dead) + return -ENODEV; + + kref_get(&ti->ref); + + filp->private_data =3D ti; + + return nonseekable_open(ino, filp); +} + +static int scmi_tlm_release(struct inode *ino, struct file *filp) +{ + struct scmi_tlm_instance *ti =3D filp->private_data; + struct device *dev =3D &ti->tsp->sdev->dev; + struct scmi_tlm_events_session *session; + + /* Any events at all for this session ?*/ + mutex_lock(&ti->sessions_mtx); + session =3D xa_erase(&ti->sessions_xa, (unsigned long)filp); + if (!session) + goto out; + + /* + * Any STILL subscribed event is cleaned-up here at the final release: + * this adddresses the case in which a userspace application had not + * properly unsubscribed OR died unexpectedly. + */ + scoped_guard(mutex, &session->events_mtx) { + unsigned long cookie =3D SCMI_TLM_EVT_XA_MIN; + struct scmi_tlm_registered_event *revt; + + /* Walk any still registered events and unsubscribe */ + xa_for_each(&session->events_xa, cookie, revt) { + int ret; + + revt =3D xa_erase(&session->events_xa, cookie); + ret =3D ti->tsp->ops->event_unsubscribe(ti->tsp->ph, + revt->type, + revt->ctx); + if (ret) + dev_warn(dev, + "Failed to unsubscribe event type:%u cookie:%lu\n", + revt->type, cookie); + + dev_dbg(dev, + "Forcibly unsubscribed event type:%u cookie:%lu\n", + revt->type, cookie); + + eventfd_ctx_put(revt->ctx); + kfree(revt); + + /* Drop the per-subscribe reference */ + kref_put(&ti->ref, scmi_tlm_instance_release); + } + + xa_destroy(&session->events_xa); + } + + kfree(session); + +out: + mutex_unlock(&ti->sessions_mtx); + + kref_put(&ti->ref, scmi_tlm_instance_release); + + return 0; +} + +static const struct file_operations stlm_fops =3D { + .owner =3D THIS_MODULE, + .open =3D scmi_tlm_open, + .release =3D scmi_tlm_release, + .unlocked_ioctl =3D scmi_tlm_unlocked_ioctl, + .compat_ioctl =3D compat_ptr_ioctl, +}; + +static void scmi_tlm_instance_release(struct kref *ref) +{ + struct scmi_tlm_instance *ti =3D kref_to_instance(ref); + struct scmi_device *sdev =3D ti->tsp->sdev; + + dev_dbg(&sdev->dev, "Finalizing Telemetry instance %u\n", ti->id); + + sdev->handle->protocol_put(sdev->handle, sdev->protocol_id); + + kfree(ti->tsp); + kfree(ti); + + /* Release the device */ + put_device(&sdev->dev); +} + +static struct scmi_tlm_instance * +scmi_tlm_instance_init(struct scmi_device *sdev, + const struct scmi_telemetry_proto_ops *ops, + struct scmi_protocol_handle *ph, unsigned int instance_id) +{ + struct scmi_tlm_instance *ti __free(kfree) =3D kzalloc_obj(*ti); + if (!ti) + return ERR_PTR(-ENOMEM); + + struct scmi_tlm_setup *tsp __free(kfree) =3D kzalloc_obj(*tsp); + if (!tsp) + return ERR_PTR(-ENOMEM); + + ti->info =3D ops->info_get(ph); + if (!ti->info) + return dev_err_ptr_probe(&sdev->dev, + -EINVAL, "invalid Telemetry info !\n"); + + /* Grab a reference */ + get_device(&sdev->dev); + + tsp->sdev =3D sdev; + tsp->ops =3D ops; + tsp->ph =3D ph; + ti->tsp =3D no_free_ptr(tsp); + + ti->id =3D instance_id; + /* Keep track of events via per-process sessions */ + xa_init(&ti->sessions_xa); + mutex_init(&ti->sessions_mtx); + + mutex_init(&ti->dead_mtx); + kref_init(&ti->ref); + + return no_free_ptr(ti); +} + +static char *scmi_tlm_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode =3D 0664; + + return kasprintf(GFP_KERNEL, "scmi/%s", dev_name(dev)); +} + +static struct class stlm_class =3D { + .name =3D "stlm", + .devnode =3D scmi_tlm_devnode, +}; + +static int scmi_telemetry_probe(struct scmi_device *sdev) +{ + const struct scmi_handle *handle =3D sdev->handle; + struct device *tdev, *dev =3D &sdev->dev; + struct scmi_protocol_handle *ph; + struct scmi_tlm_instance *ti; + unsigned int instance; + const void *ops; + int ret; + + if (!handle) + return -ENODEV; + + instance =3D handle->id; + if (instance >=3D SCMI_TLM_NUM_DEVS) + return -ENOSPC; + + ops =3D handle->protocol_get(handle, 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); + + ti =3D scmi_tlm_instance_init(sdev, ops, ph, instance); + if (IS_ERR(ti)) { + ret =3D PTR_ERR(ti); + goto err_init; + } + + cdev_init(&ti->cdev, &stlm_fops); + ti->cdev.owner =3D THIS_MODULE; + ti->devt =3D MKDEV(MAJOR(stlm_devt), MINOR(stlm_devt) + instance); + ret =3D cdev_add(&ti->cdev, ti->devt, 1); + if (ret) + goto err_cdev; + + tdev =3D device_create(&stlm_class, NULL, ti->devt, ti, "tlm_%d", ti->id); + if (IS_ERR(tdev)) { + ret =3D PTR_ERR(tdev); + goto err_tdev; + } + + dev_set_drvdata(&sdev->dev, ti); + + return 0; + +err_tdev: + cdev_del(&ti->cdev); +err_cdev: + kfree(ti->tsp); + kfree(ti); +err_init: + handle->protocol_put(handle, sdev->protocol_id); + + return ret; +} + +static void scmi_telemetry_remove(struct scmi_device *sdev) +{ + struct scmi_tlm_instance *ti; + + ti =3D dev_get_drvdata(&sdev->dev); + if (!ti) + return; + + scoped_guard(mutex, &ti->dead_mtx) + ti->dead =3D true; + + device_destroy(&stlm_class, ti->devt); + cdev_del(&ti->cdev); + + kref_put(&ti->ref, scmi_tlm_instance_release); +} + +static const struct scmi_device_id scmi_id_table[] =3D { + { SCMI_PROTOCOL_TELEMETRY, "telemetry" }, + { } +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_telemetry_driver =3D { + .name =3D SCMI_TLM_DRIVER, + .probe =3D scmi_telemetry_probe, + .remove =3D scmi_telemetry_remove, + .id_table =3D scmi_id_table, +}; + +static int __init scmi_telemetry_init(void) +{ + int ret; + + ret =3D alloc_chrdev_region(&stlm_devt, 0, SCMI_TLM_NUM_DEVS, + SCMI_TLM_DRIVER); + if (ret) + return ret; + + ret =3D class_register(&stlm_class); + if (ret) + goto err_class; + + ret =3D scmi_register(&scmi_telemetry_driver); + if (ret) + goto err_scmi; + + return 0; + +err_scmi: + class_unregister(&stlm_class); + +err_class: + unregister_chrdev_region(stlm_devt, SCMI_TLM_NUM_DEVS); + + return ret; +} +module_init(scmi_telemetry_init); + +static void __exit scmi_telemetry_exit(void) +{ + scmi_unregister(&scmi_telemetry_driver); + unregister_chrdev_region(stlm_devt, SCMI_TLM_NUM_DEVS); + class_unregister(&stlm_class); +} +module_exit(scmi_telemetry_exit); + +MODULE_AUTHOR("Cristian Marussi "); +MODULE_DESCRIPTION("ARM SCMI Telemetry Driver"); +MODULE_LICENSE("GPL"); --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9DEFD3EE1FD; Sun, 20 Sep 2026 09:21:45 +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=1789896107; cv=none; b=TfAk5myMsqhphnSvhxmu6dEVvFQWXBbCgZO1HQXFUH+8/aszde/OhTKZnNQpL6VcYhr9WISiIeh7ibxRO/hyL/CJ9y2hsFghNgCuTgEC8OC413l3y87TBTR95sdonUGotuk4uhKCytADL/8QmZ/iWVcmCk+O8VJUkadUwhk4zfs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896107; c=relaxed/simple; bh=KpUnK9nTZ9MsjB64snnJysKC48S25Nf2oKGr+4tdw/o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YEOuymbIMIveyE20KdOQXOuz6XbPOWsAk7YraHUu+sb9UpCYCxfob15DRFVxqNmwIwGVcbUHWoXo7wEzdhfhLPaWiL9zco1wSsrgXJwA7L9zT4x5ww2HbURxLEc1gHzZCj7P3NI0jtSxA1390EW4WcGeSYvqGk8r2X8q0fBpjEM= 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=kVau/WDn; 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="kVau/WDn" 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 6ECA21CE0; Sun, 20 Sep 2026 02:21:41 -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 B5A243F59E; Sun, 20 Sep 2026 02:21:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896105; bh=KpUnK9nTZ9MsjB64snnJysKC48S25Nf2oKGr+4tdw/o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kVau/WDnpjMz3xoAP2wRLiAFXpSlebj0Q1sjWx24eYjJv821CL8YqT6q3QUNwkL/j s95UoEBw4/SEq/drGyKfSaUqXjUSOewzQ8lm7c3jsNsMzPEL5KlTFurCgECtvgXKGA aGYUQObU4wcnscFNY76162qmTPtOwmbPBlk/SqmA= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Corbet , Shuah Khan Subject: [PATCH v12 22/25] docs: ioctl-number: Add SCMI Ioctls Date: Sun, 20 Sep 2026 10:19:25 +0100 Message-ID: <20260920091928.2014972-23-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 SCMI Subsystem IOCTLs numbers Cc: Jonathan Corbet Cc: Shuah Khan Cc: linux-doc@vger.kernel.org Signed-off-by: Cristian Marussi --- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documenta= tion/userspace-api/ioctl/ioctl-number.rst index 2fc53093752d..3a89b2065674 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -401,6 +401,7 @@ Code Seq# Include File = Comments 0xE5 00-3F linux/fuse.h 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h Chr= omeOS EC driver 0xEE 00-09 uapi/linux/pfrut.h Pla= tform Firmware Runtime Update and Telemetry +0xF1 00-FF uapi/linux/scmi.h ARM SCMI Subsystem 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sis= fb (in development) 0xF6 all LTT= ng Linux Trace Toolkit Next Generation --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0924B3F823D; Sun, 20 Sep 2026 09:21:49 +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=1789896112; cv=none; b=j+kmc1YboeKJ8bWfD7oCytjuZ9IoqDLTnkYYBGcXvxkNVutxhSAGnMPCwDVhs1i2hAC+2Dm6n3RJEuxGvDDk12dPpAzFpefkmpyXyuVDStlOPGyniANxO0HY2Bz3grkOC1M3jbRoubPHM/jTOQgcut2IyN+SQacDVKj/1FNILbo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896112; c=relaxed/simple; bh=KgyzAv1AKC+T5jVIxoIIFo2Oowe2Igerilum0mNQIko=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Uzq69vIjaQLfKfFgfL4tYHQBgUufmnoBqaGPrkngwH2pbRo72xYX0sTzDrGV3Qqnqr6OJl9Sp2U4iMoGwq+juKTSs9C/7Ot7LM0iG+RCxFXjcVyEKSN6GGg2zpYT9KdwFeCQMa/YHOdYOdE7aReMqAP/Y7uFVH4PR8d78TEbZQk= 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=YtLWW3YN; 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="YtLWW3YN" 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 D526C175A; Sun, 20 Sep 2026 02:21:45 -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 5284A3F59E; Sun, 20 Sep 2026 02:21:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896109; bh=KgyzAv1AKC+T5jVIxoIIFo2Oowe2Igerilum0mNQIko=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YtLWW3YNEe9swiku6k/Id0vcWG513hKLHA3xmHKWuQa06cmLp08wsWspaf2CzPkXD QQ8Dgi3dnrTPb7o2/jXacAwZ/kRzDJnlnqr9sBAz8cnLBVYRC7GHuTWrudc0WFDsxn qUNJZ5NhmTAtdmBdg8gM+HRxWLhmmtUQRJLm3mTM= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Corbet , Shuah Khan Subject: [PATCH v12 23/25] [RFC] Documentation: Add SCMI System Telemetry documentation Date: Sun, 20 Sep 2026 10:19:26 +0100 Message-ID: <20260920091928.2014972-24-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" Document ARM SCMI Telemetry IOCTLs interface. Cc: Jonathan Corbet Cc: Shuah Khan Cc: linux-doc@vger.kernel.org Signed-off-by: Cristian Marussi --- RFC since it still needs more extensive examples v6 --> v7 - removed useless top '..stlm' reference - added new IOCTLs, fixed a few grammatical errors v5 --> v6 - fixed typos --- Documentation/userspace-api/index.rst | 1 + Documentation/userspace-api/stlm.rst | 148 ++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 Documentation/userspace-api/stlm.rst diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspac= e-api/index.rst index a68b1bea57a8..22daa7ebf78e 100644 --- a/Documentation/userspace-api/index.rst +++ b/Documentation/userspace-api/index.rst @@ -54,6 +54,7 @@ Devices and I/O dcdbas vduse isapnp + stlm =20 Everything else =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D diff --git a/Documentation/userspace-api/stlm.rst b/Documentation/userspace= -api/stlm.rst new file mode 100644 index 000000000000..e30b1d1d19bd --- /dev/null +++ b/Documentation/userspace-api/stlm.rst @@ -0,0 +1,148 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +STLM - ARM SCMI Telemetry Userspace API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. contents:: + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +ARM SCMI is a System and Configuration Management protocol, based on a +client-server model, that defines a number of messages that allows a +client/agent like Linux to discover, configure and make use of services +provided by the server/platform firmware. + +SCMI v4.0 [1] introduced support for System Telemetry, through which an +agent can dynamically enumerate, configure and collect Telemetry Data +Events (DE) exposed by the platform. + +The SCMI System Telemetry driver exposes one or more dedicated devices, +named as tlm_, grouped under /dev/scmi/, one for each discovered SCMI +instance. + +An IOCTL based interface is defined in order to enumerate, configure and +read telemetry data, as succinctly described in the following. + +For more details on the uAPI, please see include/uapi/linux/scmi.h + +Resources Enumeration +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + - SCMI_TLM_GET_ABI_INFO: Gather ABI versioning detail and basic SCMI + Telemetry features like number of resources and supported features. + + - SCMI_TLM_GET_INTRVS: Enumerate available update intervals. + + - SCMI_TLM_GET_DE_INFO: Retrieve DataEvent description. + + - SCMI_TLM_GET_DE_LIST: Retrieve the list of all the existent DataEvent + descriptors. + + - SCMI_TLM_GET_GRP_LIST: Gather a list of descriptors for all defined Gro= ups. + + - SCMI_TLM_GET_GRP_INFO: Gather information for a specific Group. + + - SCMI_TLM_GET_GRP_DESC: Gather detailed group composition information for + the specified Group. + + - SCMI_TLM_GET_UUID_LIST: Gather a list of descriptors for all the SCMI + Telemetry UUIDs defined on this system. + +Configuration +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + - SCMI_TLM_GET_CFG / SCMI_TLM_SET_CFG: Get or set the whole instance, or a + specific group, configuration. + + - SCMI_TLM_GET_DE_CFG / SCMI_TLM_SET_DE_CFG: Get or set the configuration + of a specific DataEvent. + + - SCMI_TLM_GET_ALL_CFG / SCMI_TLM_SET_ALL_CFG: Get or set the cumulative + configuration of ALL the DataEvents defined on the platform. + + - SCMI_TLM_RESET: Reset the whole Telemetry configuration server side. + + - SCMI_TLM_EVENT_SUBSCRIBE: Subscribe/unsubscribe to a Telemetry event. + +Data Collection +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + - SCMI_TLM_DE_READ: Report the last updated sample for the specified + DataEvent. + + - SCMI_TLM_BULK_READ: Report the last samples for all the currently enabl= ed + DataEvents. + + - SCMI_TLM_BATCH_READ: Report the last samples for the DataEvents IDs + specified within the samples input params. + + - SCMI_TLM_SINGLE_READ: Trigger an immediate platform-side DataEvent + update and report the collected samples. + +Memory Mapped Raw Access +------------------------ + + - SCMI_TLM_GET_SHMTI_LIST: Gather a list of open file descriptors, one for + each SHMTI memory area defined for this instance, that can be used to + memory-map such areas in the calling process address space so as to be + able to directly access the SCMI Telemetry SHMTI areas shared by the + platform firmware, and parse the TDCF format. + The SCMI Telemetry TDCF format is defined in the specification at [1]. + + +Example +=3D=3D=3D=3D=3D=3D=3D + +.. code-block:: c + + int main(int argc, char **argv) + { + struct scmi_tlm_de_config des_cfg =3D {}; + struct scmi_tlm_config cfg =3D {}; + struct scmi_tlm_de_sample samples[3] =3D {}; + struct scmi_tlm_data_read data =3D {}; + int fd, ret; + + fd =3D open("/dev/scmi/tlm_0", O_RDWR); + if (fd < 0) + return fd; + + /* Enable ALL Data Events with timestamps*/ + des_cfg.enable =3D 1; + des_cfg.t_enable =3D 1; + ret =3D ioctl(fd, SCMI_TLM_SET_ALL_CFG, &des_cfg); + if (ret) + return ret; + + /* Enable Telemetry as a whole, set a 400ms update interval */ + cfg.enable =3D 1; + cfg.active.secs =3D 400; + cfg.active.exp =3D -3; + + ret =3D ioctl(fd, SCMI_TLM_SET_CFG, &cfg); + if (ret) + return ret; + + /* Read a selection of DEs */ + samples[0].id =3D 0xA001; + samples[1].id =3D 0x0002; + samples[2].id =3D 0x1010; + data.num_samples =3D 3; + data.samples =3D (unsigned long)samples; + + ret =3D ioctl(fd, SCMI_TLM_BATCH_READ, &data); + if (ret) + return ret; + + for (int i =3D 0; i < 3; i++) + fprintf(stdout, "%llu: 0x%08X -> %llu\n", + samples[i].tstamp, samples[i].id, samples[i].val); + + return 0; + } + +References +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +[1]: https://developer.arm.com/documentation/den0056/latest/ --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2EFB03D9667; Sun, 20 Sep 2026 09:21:54 +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=1789896117; cv=none; b=l8LU7aImkkZh+2S84n3jIXew84z5IjGREU0qDzEBbNw5Kq0cWVBKFzhOzZTakfovwQP7vhh96vxZPRl/fj3qwb2lnTdYkwgFAKNui2sZPw4b2aBDIrhY0oEzP0H8VjACjl/jo7FqueNgWMWc/gFFHuf7hTx9NgigLyVMKFYn2VI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896117; c=relaxed/simple; bh=csZvucxJvITzVLIEwDVRp09+kYUC6RclnZJSbwOwSqs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=P7SZO3UC9XAm6vCB6O3VcsdSGZj2KSeftIQTYiLljBHsvZ4XCwjdUhHukjf2OzUe1s3Rvblt5VQObA5XcNE4ghVOyhs0WedtGRf6JY3hGxdTElYGPOb71X7jGI/cVgky3uVvRT11EBlRSGI43pZkY+dAeSIcm73WvZKKOfLQShc= 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=FIAIZKAo; 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="FIAIZKAo" 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 2A7481C0A; Sun, 20 Sep 2026 02:21:50 -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 B94723F59E; Sun, 20 Sep 2026 02:21:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896113; bh=csZvucxJvITzVLIEwDVRp09+kYUC6RclnZJSbwOwSqs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FIAIZKAo2P+WfY0KsPLPvamKlSt8GbeUHWVyktii415ifZje4HJsiUcuiXYC690Vg WTKrdK6G9QawsLSfDotoqq0xm9yWxxM11RTHlkxZTtlr4sMMfYhsHjwMTD+SBlXSGO IdO0qVBHMMWD9/gTt8r0G9OoHCBYKd9pTOn7W6vw= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 24/25] [RFC] tools/scmi: Add SCMI Telemetry testing tool Date: Sun, 20 Sep 2026 10:19:27 +0100 Message-ID: <20260920091928.2014972-25-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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 a testing tool that exercises the SCMI ioctls UAPI interface: as of now the tool simply queries the initial state of the SCMI Telemetry subsystem, tries to enable all the existent Data Events and dumps all the Telemetry data. Signed-off-by: Cristian Marussi --- v11 --> v12 - renamed command helpers - reviewed all internal/external error codes handling - fixed dump_shmti read loop - fixed shmti_check end address calculation - fixed batch read boundary checks - fixed group indexing v10 --> v11 - add query for ALL new 0-sized commands: GET_INTRVS/BULK/SINGLE v8 --> v9 - move to new _LIST IOCTLs support based on scmi_tlm_batch container - add a "query" command to issue 0-items IOCTLs requests v7 -> v8 - add monitor detached option v6 --> v7 - added monitor command for events - using and exposing DE's UUID/SID/OFFSET - use BATCH READ with status - use BATCH DE CONFIG GET/SET - use new ABI/SCMI feats flags v5 --> v6 - carry-on on error if feats NOT supported - build BATCH_READ with enumerated values - new UAPI naming - reworked UUID print - added info/shmti interactive cmds - added cfg/single/bulk/batch interactive cmds - added reset - added tlm enable - using new GET_ABI_INFO - moved DE_READ per-de sampling away from get_state v4 --> v5 - changes to adapt to new chardev/IOCTLs ABI - change IOCTLs param to avoid flexible arrays - added SHMTIs mmap dump v3 --> v4 - added generation file support --- tools/testing/scmi/Makefile | 25 + tools/testing/scmi/stlm.c | 1510 +++++++++++++++++++++++++++++++++++ 2 files changed, 1535 insertions(+) create mode 100644 tools/testing/scmi/Makefile create mode 100644 tools/testing/scmi/stlm.c diff --git a/tools/testing/scmi/Makefile b/tools/testing/scmi/Makefile new file mode 100644 index 000000000000..a6a101f8398b --- /dev/null +++ b/tools/testing/scmi/Makefile @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +CC?=3D$(CROSS_COMPILE)gcc +OBJS =3D stlm.o + +CFLAGS=3D-Wall -static -std=3Dgnu11 -I ../../../include/uapi/ +ifneq ($(DEBUG), ) + CFLAGS+=3D-O0 -g -ggdb +else + CFLAGS+=3D-static +endif + +all: stlm + +stlm: $(OBJS) + $(CC) $(CFLAGS) $^ -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +clean: + rm -f *.o + rm -f stlm + +.PHONY: clean diff --git a/tools/testing/scmi/stlm.c b/tools/testing/scmi/stlm.c new file mode 100644 index 000000000000..76735651310f --- /dev/null +++ b/tools/testing/scmi/stlm.c @@ -0,0 +1,1510 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define SLEEP_MS 3000 + +#define IOCTL_ERR_STR(_ioctl) "IOCTL:" #_ioctl + +#define USAGE_STRING \ + "\t[OPTIONS]\n" \ + "\t -i \t\tUse this device instance path. Default: /dev/sc= mi/tlm_0\n" \ + "\t[COMMANDS]\n" \ + "\t info [full] - Display summarized (or full) configuration and informat= ion data\n" \ + "\t query - Query ALL possible IOCTLs for resources (item=3D0)\n" \ + "\t uuids - UUIDs dump\n" \ + "\t monitor [detach] - Monitor Generation counter\n" \ + "\t shmti [check][dump ] - SHMTI operations\n" \ + "\t\t check - Sanity check of all SHMTis\n" \ + "\t\t dump [] - Binary dump of or all SHMTI areas\n" \ + "\t des - Display detailed DEs info and config data\n" \ + "\t groups - Display detailed GROUPS info and config data\n" \ + "\t cfg [SUB-COMMANDS] - Configure Telemetry\n" \ + "\t\t tlm on|off - Enable or disable Telemetry as a whole\n" \ + "\t\t update - Set global update interval\n" \ + "\t\t all_des [[on|off]:[on|off]] - Enable or disable ALL DEs with or wou= t TS\n" \ + "\t\t de [ [[on|off]:[on|off]]] - Get/Set configuration of one or = ALL de\n" \ + "\t\t group [] [[on|off]:[on|off]:[]] - Get/Set GRO= UP configuration\n" \ + "\t single - Perform a single sample read\n" \ + "\t bulk - Perform a bulk read\n" \ + "\t batch [de_id [de_id]... ] - Perform a batch read of the DE ID= s listed in input\n" \ + "\t reset - Perform a Telemetry subsytem reset platform-side\n" + +struct tlm_de { + struct scmi_tlm_de_info *info; + struct scmi_tlm_de_config cfg; + struct scmi_tlm_de_sample sample; +}; + +struct tlm_group { + int fd; + struct scmi_tlm_grp_info *info; + struct scmi_tlm_grp_desc *desc; + struct scmi_tlm_intervals *ivs; +}; + +struct tlm_state { + int fd; + const char *path; + struct scmi_tlm_abi_info info; + struct scmi_tlm_config cfg; + struct scmi_tlm_intervals *ivs; + struct scmi_tlm_batch *shmtis; + unsigned int num_des; + struct tlm_de *des; + unsigned int num_groups; + struct tlm_group *grps; +}; + +struct parsed_args { + const char *instance; + const char *cmd; + unsigned int cnt; + char **opts; +}; + +static inline void cmd_dump_des(struct tlm_state *st) +{ + fprintf(stdout, "\n+ Found #%d DEs:\n", st->num_des); + for (int i =3D 0; i < st->num_des; i++) { + fprintf(stdout, "\t[0x%08X][%s]\n", + st->des[i].info->id, st->des[i].info->name); + fprintf(stdout, "\t\tGRP_ID:0x%08X SZ:%u TYPE:%u UNIT:%u UNIT_EXP:%d= TS_RATE:%d\n", + st->des[i].info->grp_id, st->des[i].info->data_sz, + st->des[i].info->type, st->des[i].info->unit, + st->des[i].info->unit_exp, st->des[i].info->ts_rate); + fprintf(stdout, "\t\tINST_ID:%u COMPO_ID:%u COMPO_TYPE:%u PERSIST:%u\= n\n", + st->des[i].info->instance_id, st->des[i].info->compo_instance_id, + st->des[i].info->compo_type, st->des[i].info->persistent); + } + fprintf(stdout, "\n"); + + for (int i =3D 0; i < st->num_des; i++) { + struct tlm_de *de =3D &st->des[i]; + + /* Collect fresh sample */ + if (de->cfg.enable) { + int ret; + + de->sample.id =3D de->info->id; + ret =3D ioctl(st->fd, SCMI_TLM_DE_READ, &de->sample); + if (ret) + perror(IOCTL_ERR_STR(SCMI_TLM_DE_READ)); + } + + fprintf(stdout, "\t[0x%08X][%s]\t\t%s %s\n\t\t-->> TS:%16llu %016llX\n", + de->info->id, de->info->name, + de->cfg.enable ? "ON" : "OFF", + de->cfg.t_enable ? "/TS_ON " : "/TS_OFF", + de->sample.tstamp, de->sample.val); + } + fprintf(stdout, "\n"); +} + +static inline void cmd_dump_groups(struct tlm_state *st) +{ + fprintf(stdout, "\n+ Found %d GRPs: ", st->num_groups); + for (int i =3D 0; i < st->num_groups; i++) { + struct scmi_tlm_update_interval *grp_intrvs; + struct tlm_group *grp =3D &st->grps[i]; + uint32_t *composing_des; + + fprintf(stdout, "\n\tGRP_ID:%d DES#:%d INTRVS#:%d %s\n", + grp->info->grp_id, grp->info->num_des, + grp->info->num_intervals, + (grp->ivs->flags & SCMI_TLM_INTERV_DISCRETE) ? "(DISCRETE)" : ""); + + fprintf(stdout, "\tGRP Intervals:\n"); + grp_intrvs =3D (struct scmi_tlm_update_interval *)grp->ivs->intervals; + for (int j =3D 0; j < grp->ivs->num_intervals; j++) + fprintf(stdout, "\t[%d]::%u,%d\n", j, + grp_intrvs[j].secs, grp_intrvs[j].exp); + + composing_des =3D (uint32_t *)grp->desc->composing_des; + fprintf(stdout, "\tCOMPOSING_DES:"); + for (int j =3D 0; j < grp->desc->num_des; j++) + fprintf(stdout, "0x%08X ", composing_des[j]); + fprintf(stdout, "\n"); + } +} + +static void print_uuid(const char *prefix, void *arg) +{ + unsigned char *uuid =3D arg; + + fprintf(stdout, "%s", prefix); + for (int i =3D 0; i < SCMI_TLM_DE_IMPL_UUID_SZ; i++) { + fprintf(stdout, "%02X", uuid[i]); + switch (i) { + case 3: + case 5: + case 7: + case 9: + fprintf(stdout, "%c", '-'); + break; + case 15: + fprintf(stdout, "%c", '\n'); + break; + } + } +} + +static inline void cmd_dump_state(struct tlm_state *st, struct parsed_args= *args) +{ + struct scmi_tlm_update_interval *intervals; + struct scmi_tlm_shmti_info *shinfo; + + fprintf(stdout, "- SYSTEM TELEMETRY @instance: %s\n\n", st->path); + fprintf(stdout, "+ ABI Version: %u\n", st->info.abi_version); + fprintf(stdout, "+ ABI Features: 0x%04X\n", st->info.abi_features); + fprintf(stdout, "\t (RESET:%u", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_RESET)); + fprintf(stdout, " EVENT:%u", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_EVENT)); + fprintf(stdout, " UUID_LIST:%u", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_UUID_LIST)); + fprintf(stdout, " BATCH_STATE:%u", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_BATCH_STATE)); + fprintf(stdout, " BATCHED_CFG:%u", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_BATCHED_CFG)); + fprintf(stdout, " DE_TRACKING:%u)\n", + !!(st->info.abi_features & SCMI_TLM_ABI_FEAT_DE_TRACKING)); + fprintf(stdout, "+ SCMI Features: 0x%04X\n", st->info.features); + fprintf(stdout, "\t (RESET:%u", + !!(st->info.features & SCMI_TLM_SCMI_SUPPORT_RESET)); + fprintf(stdout, " SINGLE_SAMPLE:%u", + !!(st->info.features & SCMI_TLM_SCMI_SUPPORT_SINGLE_SAMPLE)); + fprintf(stdout, " GROUP_CONFIG:%u", + !!(st->info.features & SCMI_TLM_SCMI_SUPPORT_GROUP_CONFIG)); + fprintf(stdout, " UPDATE_NOTIFICATIONS:%u)\n", + !!(st->info.features & SCMI_TLM_SCMI_SUPPORT_UPDATE_NOTIFICATION)); + fprintf(stdout, "+ DEs#: %d\n", st->info.num_des); + fprintf(stdout, "+ GRPS#: %d\n", st->info.num_groups); + fprintf(stdout, "+ INTRV#: %d %s\n", st->info.num_intervals, + (st->ivs->flags & SCMI_TLM_INTERV_DISCRETE) ? "(DISCRETE)" : ""); + fprintf(stdout, "+ SHMTIS#: %d\n", st->info.num_shmtis); + + print_uuid("+ Primary UUID: ", &st->info.primary_de_impl_version[0]); + fprintf(stdout, "\n+ TLM_ENABLED: %d\n", st->cfg.enable); + fprintf(stdout, "+ CURRENT_UPDATE_INTERVAL: %u,%d\n", + st->cfg.active.secs, st->cfg.active.exp); + + fprintf(stdout, "\n+ Found #%u Global Update Intervals\n", + st->ivs->num_intervals); + intervals =3D (struct scmi_tlm_update_interval *)st->ivs->intervals; + for (int i =3D 0; i < st->ivs->num_intervals; i++) + fprintf(stdout, "\t[%d]::%u,%d\n", i, + intervals[i].secs, intervals[i].exp); + + fprintf(stdout, "\n+ Found #%u SHMTI areas\n", st->shmtis->num_items); + shinfo =3D (struct scmi_tlm_shmti_info *)st->shmtis->items; + for (int i =3D 0; i < st->shmtis->num_items; i++) + fprintf(stdout, "\t[%d]:: FD:%u LEN:%u\n", + shinfo[i].sid, shinfo[i].fd, shinfo[i].len); + + if (st->info.num_des !=3D st->num_des) { + fprintf(stdout, "\n++++++ DES NOT FULLY_ENUMERATED ++++++\n"); + fprintf(stdout, "+++ DECLARED:%u ENUMERATED:%u +++\n", + st->info.num_des, st->num_des); + } + + if (args && args->cnt && args->opts && + !strncmp(args->opts[0], "full", strlen(args->opts[0]))) { + cmd_dump_des(st); + cmd_dump_groups(st); + } +} + +static int discover_base_info(int fd, struct scmi_tlm_abi_info *info) +{ + int ret; + + info->size =3D sizeof(*info); + ret =3D ioctl(fd, SCMI_TLM_GET_ABI_INFO, info); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_ABI_INFO)); + return -errno; + } + + return 0; +} + +static int +query_items(int fd, unsigned long ioctl_op, size_t item_sz, const char *ta= g) +{ + struct scmi_tlm_batch query =3D {}; + int ret; + + query.item_sz =3D item_sz; + ret =3D ioctl(fd, ioctl_op, &query); + if (ret < 0) { + perror(tag); + return -errno; + } + + fprintf(stdout, "--- [QUERY]:[%s] -> NUM_ITEMS:%u\n", + tag, query.num_items); + + return 0; +} + +static inline struct scmi_tlm_batch * +batch_buffer_alloc(int num_items, size_t item_sz, bool need_status) +{ + struct scmi_tlm_batch *batch; + size_t batch_sz, all_items_sz; + void *items; + + batch_sz =3D sizeof(*batch); + batch =3D malloc(batch_sz); + if (!batch) + return NULL; + + bzero(batch, batch_sz); + batch->num_items =3D num_items; + + batch->item_sz =3D item_sz; + all_items_sz =3D batch->num_items * batch->item_sz; + items =3D malloc(all_items_sz); + if (!items) { + free(batch); + return NULL; + } + + bzero(items, all_items_sz); + batch->items =3D (uint64_t)items; + + if (need_status) { + int *states; + size_t states_sz =3D sizeof(*states) * batch->num_items; + + states =3D malloc(states_sz); + if (!states) + return NULL; + + bzero(states, states_sz); + batch->states =3D (uint64_t)states; + } + + return batch; +} + +static struct scmi_tlm_batch *scmi_get_des_list(int fd, int num_des) +{ + struct scmi_tlm_de_info *dei; + struct scmi_tlm_batch *batch; + int ret; + + batch =3D batch_buffer_alloc(num_des, sizeof(*dei), false); + if (!batch) + return NULL; + + ret =3D ioctl(fd, SCMI_TLM_GET_DE_LIST, batch); + if (ret) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_LIST)); + return NULL; + } + + return batch; +} + +static struct tlm_de *enumerate_des(struct tlm_state *st) +{ + struct scmi_tlm_batch *des_batch; + struct tlm_de *des; + int num_items; + + des_batch =3D scmi_get_des_list(st->fd, st->info.num_des); + if (!des_batch) + return NULL; + + num_items =3D des_batch->num_items; + des =3D malloc(sizeof(*des) * num_items); + if (!des) + return NULL; + + bzero(des, sizeof(*des) * num_items); + + for (int i =3D 0; i < num_items; i++) { + struct tlm_de *de =3D &des[i]; + struct scmi_tlm_batch batch =3D {}; + int ret; + + de->info =3D &(((struct scmi_tlm_de_info *)des_batch->items)[i]); + de->cfg.id =3D de->info->id; + + batch.num_items =3D 1; + batch.item_sz =3D sizeof(de->cfg); + batch.items =3D (uint64_t)&de->cfg; + + ret =3D ioctl(st->fd, SCMI_TLM_GET_DE_CFG, &batch); + if (ret) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_CFG)); + return NULL; + } + } + + /* On failure st->num_des remains zero */ + st->num_des =3D num_items; + + return des; +} + +static int get_current_config(int fd, struct scmi_tlm_config *cfg) +{ + int ret; + + ret =3D ioctl(fd, SCMI_TLM_GET_CFG, cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_CFG)); + return -errno; + } + + return 0; +} + +static struct scmi_tlm_batch *scmi_get_grps_list(int fd, int num_groups) +{ + struct scmi_tlm_batch *batch; + int ret; + + batch =3D batch_buffer_alloc(num_groups, sizeof(struct scmi_tlm_grp_info), + false); + if (!batch) + return NULL; + + ret =3D ioctl(fd, SCMI_TLM_GET_GRP_LIST, batch); + if (ret) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_GRP_LIST)); + free(batch); + return NULL; + } + + return batch; +} + +static struct scmi_tlm_intervals * +enumerate_intervals(int fd, int num_intervals, unsigned int *grp_id) +{ + struct scmi_tlm_update_interval *intervs; + struct scmi_tlm_intervals *ivs; + size_t sz, i_sz; + int ret; + + sz =3D sizeof(*ivs); + ivs =3D malloc(sz); + if (!ivs) + return NULL; + + bzero(ivs, sz); + + i_sz =3D sizeof(*intervs) * num_intervals; + intervs =3D malloc(i_sz); + if (!intervs) { + free(ivs); + return NULL; + } + + bzero(intervs, i_sz); + + ivs->num_intervals =3D num_intervals; + ivs->intervals =3D (uint64_t)intervs; + if (grp_id) { + ivs->grp_id =3D *grp_id; + ivs->flags =3D SCMI_TLM_INTERV_GROUP; + } + + ret =3D ioctl(fd, SCMI_TLM_GET_INTRVS, ivs); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_INTRVS)); + free(ivs); + free(intervs); + return NULL; + } + + return ivs; +} + +static struct scmi_tlm_batch *enumerate_shmtis(struct tlm_state *st) +{ + struct scmi_tlm_batch *batch; + int ret; + + batch =3D batch_buffer_alloc(st->info.num_shmtis, + sizeof(struct scmi_tlm_shmti_info), false); + if (!batch) + return NULL; + + ret =3D ioctl(st->fd, SCMI_TLM_GET_SHMTI_LIST, batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_SHMTI_LIST)); + free(batch); + return NULL; + } + + return batch; +} + +static struct tlm_group *enumerate_groups(struct tlm_state *st) +{ + struct scmi_tlm_batch *grps_batch; + struct tlm_group *grps; + int num_items; + + grps_batch =3D scmi_get_grps_list(st->fd, st->info.num_groups); + if (!grps_batch) + return NULL; + + num_items =3D grps_batch->num_items; + grps =3D malloc(sizeof(*grps) * num_items); + if (!grps) + return NULL; + + bzero(grps, sizeof(*grps) * num_items); + for (int i =3D 0; i < num_items; i++) { + struct tlm_group *grp =3D &grps[i]; + size_t size, composing_sz; + uint32_t *composing_des; + int ret; + + grp->info =3D &(((struct scmi_tlm_grp_info *)grps_batch->items)[i]); + size =3D sizeof(*grp->desc); + grp->desc =3D malloc(size); + if (!grp->desc) + return NULL; + + bzero(grp->desc, size); + composing_sz =3D sizeof(uint32_t) * grp->info->num_des; + composing_des =3D malloc(composing_sz); + if (!composing_des) + return NULL; + + grp->desc->grp_id =3D grp->info->grp_id; + grp->desc->num_des =3D grp->info->num_des; + grp->desc->composing_des =3D (uint64_t)composing_des; + ret =3D ioctl(st->fd, SCMI_TLM_GET_GRP_DESC, grp->desc); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_GRP_DESC)); + return NULL; + } + + grp->ivs =3D enumerate_intervals(st->fd, grp->info->num_intervals, + &grp->info->grp_id); + } + + /* On failure st->num_groups remains zero */ + st->num_groups =3D num_items; + + return grps; +} + +static int gather_tlm_state(struct tlm_state *st) +{ + int ret; + + ret =3D discover_base_info(st->fd, &st->info); + if (ret) + return ret; + + st->ivs =3D enumerate_intervals(st->fd, st->info.num_intervals, NULL); + if (!st->ivs) + return -1; + + ret =3D get_current_config(st->fd, &st->cfg); + if (ret) + return ret; + + if (st->info.num_des) { + st->des =3D enumerate_des(st); + if (!st->des) + fprintf(stderr, "Failed to enumerate DES\n"); + } + + if (st->info.num_groups) { + st->grps =3D enumerate_groups(st); + if (!st->grps) + fprintf(stderr, "Failed to enumerate GROUPS\n"); + } + + if (st->info.num_shmtis) { + st->shmtis =3D enumerate_shmtis(st); + if (!st->shmtis) { + fprintf(stderr, "Failed to enumerate SHMTIS\n"); + st->info.num_shmtis =3D 0; + } + } + + return 0; +} + +static struct tlm_state *open_session(const char *path) +{ + struct tlm_state *st; + + st =3D malloc(sizeof(*st)); + if (!st) + return NULL; + + bzero(st, sizeof(*st)); + st->fd =3D open(path, O_RDWR); + if (st->fd < 0) { + perror("open"); + free(st); + return NULL; + } + + st->path =3D path; + + return st; +} + +static int dump_shmti(struct scmi_tlm_shmti_info *shmti) +{ + ssize_t read_bytes =3D 0; + void *tdcf; + int len; + + tdcf =3D mmap(NULL, shmti->len, PROT_READ, MAP_SHARED, shmti->fd, 0); + if (tdcf =3D=3D MAP_FAILED) { + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } + + tdcf +=3D shmti->offset; + len =3D shmti->len - shmti->offset; + do { + ssize_t bytes; + + bytes =3D write(1, tdcf + read_bytes, len - read_bytes); + if (bytes < 0) + return -1; + read_bytes +=3D bytes; + } while (read_bytes < len); + + return read_bytes; +} + +static int shmti_dump(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_shmti_info *shmti; + unsigned long first, last; + + if (!st->shmtis->num_items) + return 0; + + if (args->cnt > 1) { + first =3D last =3D strtoul(args->opts[1], NULL, 0); + if (first =3D=3D ULONG_MAX || errno =3D=3D EINVAL || + first >=3D st->shmtis->num_items) + return -1; + } else { + first =3D 0; + last =3D st->shmtis->num_items - 1; + } + + for (int sid =3D first; sid <=3D last; sid++) { + int ret; + + shmti =3D &(((struct scmi_tlm_shmti_info *)(st->shmtis->items))[sid]); + fprintf(stderr, "Binary DUMP of SHMTI[%u] LEN:%u\n", + shmti->sid, shmti->len); + + ret =3D dump_shmti(shmti); + if (!ret) + fprintf(stderr, "...FAILED DUMP of SHMTI[%u] !!!\n", sid); + } + + return 0; +} + +static int shmti_check(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_shmti_info *shmtis; + + shmtis =3D (struct scmi_tlm_shmti_info *)st->shmtis->items; + + fprintf(stdout, "\n- SHMTI mmap SANITY CHECK -\n-------------------\n"); + + for (int i =3D 0; i < st->shmtis->num_items; i++) { + unsigned long *start, *end; + void *tdcf; + char *buf; + + fprintf(stdout, "Mapping SHMTI[%u] - SID:%u LEN:%u FD:%u OFFS:%u\n", + i, shmtis[i].sid, shmtis[i].len, shmtis[i].fd, shmtis[i].offset); + + tdcf =3D mmap(NULL, shmtis[i].len, PROT_READ, MAP_SHARED, + shmtis[i].fd, 0); + if (tdcf =3D=3D MAP_FAILED) { + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } + + start =3D tdcf + shmtis[i].offset; + end =3D tdcf + shmtis[i].len; + + fprintf(stdout, "-----------------------------------------------------\n= "); + fprintf(stdout, "Mapped SHMTI - SID:%u %u@%p\n", + shmtis[i].sid, shmtis[i].offset, tdcf); + + buf =3D (char *)start; + for (int j =3D 0; j < 4; j++) + fprintf(stdout, "%c ", buf[j]); + + fprintf(stdout, " =3D=3D=3D> "); + + buf =3D ((char *)end) - 4; + for (int j =3D 0 ; j < 4; j++) + fprintf(stdout, "%c ", buf[j]); + fprintf(stdout, "\n-----------------------------------------------------= \n"); + } + + return 0; +} + +static int cmd_shmti_ops(struct tlm_state *st, struct parsed_args *args) +{ + if (!args->cnt || !args->opts) + return -1; + + if (!strncmp(args->opts[0], "dump", strlen(args->opts[0]))) + return shmti_dump(st, args); + if (!strncmp(args->opts[0], "check", strlen(args->opts[0]))) + return shmti_check(st, args); + + return -1; +} + +static int parse_options(int argc, char **argv, struct parsed_args *args) +{ + int opt =3D 0; + + /* Set defaults */ + args->instance =3D "/dev/scmi/tlm_0"; + while ((opt =3D getopt(argc, argv, "i:h")) !=3D -1) { + switch (opt) { + case 'i': + args->instance =3D optarg; + break; + case 'h': + default: + return -EINVAL; + } + } + + if (optind <=3D argc) { + if (optind < argc - 1) { + args->cnt =3D argc - 1 - optind; + args->opts =3D malloc(sizeof(*args->opts) * args->cnt); + if (!args->opts) + return -1; + } + + args->cmd =3D argv[optind]; + for (int i =3D 0, j =3D optind + 1; j < argc; i++, j++) + args->opts[i] =3D argv[j]; + } + + return 0; +} + +static inline void usage(char *program_name) +{ + printf("Usage: %s [OPTIONS] [CMD [CMD_OPTS]...]\n", program_name); + printf("%s", USAGE_STRING); +} + +static inline struct scmi_tlm_data_read *bulk_buffer_alloc(struct tlm_stat= e *st) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_data_read *bulk; + size_t bulk_sz, samples_sz; + + bulk_sz =3D sizeof(*bulk); + bulk =3D malloc(bulk_sz); + if (!bulk) + return NULL; + + bzero(bulk, bulk_sz); + samples_sz =3D st->info.num_des * sizeof(*samples); + samples =3D malloc(samples_sz); + if (!samples) { + free(bulk); + return NULL; + } + + bzero(samples, samples_sz); + bulk->samples =3D (uint64_t)samples; + bulk->num_samples =3D st->info.num_des; + + return bulk; +} + +static int cmd_single_read(struct tlm_state *st) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_data_read *bulk; + int ret; + + bulk =3D bulk_buffer_alloc(st); + if (!bulk) + return -1; + + ret =3D ioctl(st->fd, SCMI_TLM_SINGLE_READ, bulk); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SINGLE_READ)); + return -errno; + } + + fprintf(stdout, "\n- Single ASYNC read -\n-------------------\n"); + samples =3D (struct scmi_tlm_de_sample *)bulk->samples; + for (int i =3D 0; i < bulk->num_samples; i++) + fprintf(stdout, "0x%08X %016llu %016llX\n", + samples[i].id, samples[i].tstamp, samples[i].val); + + return 0; +} + +static int cmd_bulk_read(struct tlm_state *st) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_data_read *bulk; + int ret; + + bulk =3D bulk_buffer_alloc(st); + if (!bulk) + return -1; + + ret =3D ioctl(st->fd, SCMI_TLM_BULK_READ, bulk); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_BULK_READ)); + return -errno; + } + + fprintf(stdout, "\n- BULK read -\n-------------------\n"); + samples =3D (struct scmi_tlm_de_sample *)bulk->samples; + for (int i =3D 0; i < bulk->num_samples; i++) + fprintf(stdout, "0x%08X %016llu %016llX\n", + samples[i].id, samples[i].tstamp, samples[i].val); + + return 0; +} + +static int cmd_tlm_reset(struct tlm_state *st) +{ + int ret; + + ret =3D ioctl(st->fd, SCMI_TLM_RESET); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_RESET)); + return -errno; + } + + fprintf(stdout, "\n--- TLM RESET ISSUED\n"); + + return 0; +} + +static int cmd_batch_read(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_batch *batch; + int ret; + + if (!args->opts || !args->cnt) + return -EINVAL; + + if (args->cnt > st->info.num_des) + return -ENOSPC; + + /* Allocate for full size with status */ + batch =3D batch_buffer_alloc(st->info.num_des, sizeof(*samples), true); + if (!batch) + return -1; + + samples =3D (struct scmi_tlm_de_sample *)batch->items; + batch->num_items =3D args->cnt; + for (int i =3D 0; i < batch->num_items; i++) { + unsigned long val; + + val =3D strtoul(args->opts[i], NULL, 0); + if (val =3D=3D ULONG_MAX || errno =3D=3D EINVAL) + continue; + + samples[i].id =3D (unsigned int)val; + } + + ret =3D ioctl(st->fd, SCMI_TLM_BATCH_READ, batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_BATCH_READ)); + return -errno; + } + + fprintf(stdout, "\n- BATCH read -\n-------------------\n"); + for (int i =3D 0; i < batch->num_items; i++) + fprintf(stdout, "STATUS[%d] =3D> 0x%08X %016llu %016llX\n", + ((uint32_t *)batch->states)[i], + samples[i].id, samples[i].tstamp, samples[i].val); + + return 0; +} + +/* + * :[:] + */ +static void +parse_item_cfg(char *str, bool *ena, bool *t_ena, unsigned int *update) +{ + char *tok; + + if (!ena) + return; + + tok =3D strtok(str, ":"); + if (tok) + *ena =3D strncmp(tok, "on", strlen(tok)) =3D=3D 0; + + if (!t_ena) + return; + + tok =3D strtok(NULL, ":"); + if (tok) + *t_ena =3D strncmp(tok, "on", strlen(tok)) =3D=3D 0; + + if (!update) + return; + + tok =3D strtok(NULL, ":"); + if (tok) { + unsigned long val; + + val =3D strtoul(tok, NULL, 0); + if (val =3D=3D ULONG_MAX || errno =3D=3D EINVAL) + return; + + *update =3D (unsigned int)val; + } +} + +static int configure_all_des(struct tlm_state *st, struct parsed_args *arg= s) +{ + struct scmi_tlm_de_config de_cfg =3D {}; + bool ena, t_ena; + int ret; + + if (!args->opts) + return -1; + + ret =3D ioctl(st->fd, SCMI_TLM_GET_ALL_CFG, &de_cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_ALL_CFG)); + return -errno; + } + + fprintf(stdout, "--- [GET] ALL DEs ON:%d T_ON:%d\n", + de_cfg.enable, de_cfg.t_enable); + + if (args->cnt < 2) + return 0; + + parse_item_cfg(args->opts[1], &ena, &t_ena, NULL); + de_cfg.enable =3D !!ena; + de_cfg.t_enable =3D !!t_ena; + + fprintf(stdout, "--- [SET] ALL DEs ON:%d T_ON:%d\n", + de_cfg.enable, de_cfg.t_enable); + + ret =3D ioctl(st->fd, SCMI_TLM_SET_ALL_CFG, &de_cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SET_ALL_CFG)); + return -errno; + } + + return 0; +} + +static int de_config_batch_get(struct tlm_state *st, struct scmi_tlm_batch= *batch) +{ + int ret; + + /* Get current config */ + ret =3D ioctl(st->fd, SCMI_TLM_GET_DE_CFG, batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_CFG)); + return -errno; + } + + return 0; +} + +static int configure_de(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_de_config *de_cfg; + struct scmi_tlm_batch *batch; + bool ena, t_ena; + unsigned long val; + int ret, *states; + + if (!args->opts) + return -1; + + if (args->cnt < 2) { + batch =3D batch_buffer_alloc(st->num_des, sizeof(*de_cfg), true); + if (!batch) + return -1; + + /* Dump all DE configs */ + de_cfg =3D (struct scmi_tlm_de_config *)batch->items; + for (int i =3D 0; i < st->num_des; i++) + de_cfg[i].id =3D st->des[i].info->id; + + ret =3D de_config_batch_get(st, batch); + if (ret) + return ret; + + states =3D (int *)batch->states; + for (int i =3D 0; i < batch->num_items; i++) { + fprintf(stdout, + "--- [GET] STATUS[%d] -> DE:0x%08X ON:%d T_ON:%d SID:%u OFFS:%u", + states[i], de_cfg[i].id, de_cfg[i].enable, + de_cfg[i].t_enable, de_cfg[i].sid, de_cfg[i].offset); + print_uuid(" UUID: ", &de_cfg[i].uuid[0]); + fprintf(stdout, "\n"); + } + + return 0; + } + + /*Dump one DE config */ + batch =3D batch_buffer_alloc(1, sizeof(*de_cfg), true); + if (!batch) + return -1; + + states =3D (int *)batch->states; + de_cfg =3D (struct scmi_tlm_de_config *)batch->items; + val =3D strtoul(args->opts[1], NULL, 0); + if (val =3D=3D ULONG_MAX || errno =3D=3D EINVAL) + return -1; + + de_cfg[0].id =3D (unsigned int)val; + ret =3D de_config_batch_get(st, batch); + if (ret) + return ret; + + fprintf(stdout, "--- [GET] STATUS[%d] -> DE:0x%08X ON:%d T_ON:%d SID:%= u OFFS:%u", + states[0], de_cfg[0].id, de_cfg[0].enable, + de_cfg[0].t_enable, de_cfg[0].sid, de_cfg[0].offset); + print_uuid(" UUID: ", &de_cfg[0].uuid[0]); + fprintf(stdout, "\n"); + + if (args->cnt < 3) + return 0; + + /* Configure one DE */ + parse_item_cfg(args->opts[2], &ena, &t_ena, NULL); + de_cfg[0].enable =3D !!ena; + de_cfg[0].t_enable =3D !!t_ena; + + ret =3D ioctl(st->fd, SCMI_TLM_SET_DE_CFG, batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SET_DE_CFG)); + return -errno; + } + + fprintf(stdout, "--- [SET] STATUS[%d] -> DE:0x%08X ON:%d T_ON:%d SID:%= u OFFS:%u", + states[0], de_cfg[0].id, de_cfg[0].enable, + de_cfg[0].t_enable, de_cfg[0].sid, de_cfg[0].offset); + print_uuid(" UUID: ", &de_cfg[0].uuid[0]); + fprintf(stdout, "\n"); + + return 0; +} + +static int grp_config_get(struct tlm_state *st, struct scmi_tlm_config *cf= g) +{ + int ret; + + /* Get current config */ + ret =3D ioctl(st->fd, SCMI_TLM_GET_CFG, cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_CFG)); + return -errno; + } + + return 0; +} + +static int configure_group(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_config cfg =3D {}; + bool ena, t_ena; + unsigned long val; + int ret; + + if (!args->opts) + return -1; + + if (args->cnt < 2) { + /* Dump all GRPS configs */ + for (int i =3D 0; i < st->num_groups; i++) { + cfg.grp_id =3D st->grps[i].info->grp_id; + cfg.active.exp =3D -3; + cfg.flags =3D SCMI_TLM_CONFIG_GROUP; + ret =3D grp_config_get(st, &cfg); + if (!ret) + fprintf(stdout, + "--- [GET] -> GROUP:0x%08X ON:%d T_ON:%d INTRV:%u,%d\n", + cfg.grp_id, cfg.enable, cfg.t_enable, + cfg.active.secs, cfg.active.exp); + } + + return 0; + } + + /*Dump one GROUP config */ + val =3D strtoul(args->opts[1], NULL, 0); + if (val =3D=3D ULONG_MAX || errno =3D=3D EINVAL) + return -1; + + cfg.grp_id =3D (unsigned int)val; + cfg.flags =3D SCMI_TLM_CONFIG_GROUP; + cfg.active.exp =3D -3; + ret =3D grp_config_get(st, &cfg); + if (ret) + return ret; + + fprintf(stdout, "--- [GET] -> GROUP:0x%08X ON:%d T_ON:%d INTRV:%u,%d\n= ", + cfg.grp_id, cfg.enable, cfg.t_enable, cfg.active.secs, cfg.active.exp); + + if (args->cnt < 3) + return 0; + + /* Configure one GROUP */ + parse_item_cfg(args->opts[2], &ena, &t_ena, &cfg.active.secs); + cfg.enable =3D !!ena; + cfg.t_enable =3D !!t_ena; + + fprintf(stdout, "--- [SET] -> GROUP:0x%08X ON:%d T_ON:%d INTRV:%u,%d\n= ", + cfg.grp_id, cfg.enable, cfg.t_enable, + cfg.active.secs, cfg.active.exp); + + ret =3D ioctl(st->fd, SCMI_TLM_SET_CFG, &cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SET_CFG)); + return -errno; + } + + return 0; +} + +static int configure_telemetry(struct tlm_state *st, struct parsed_args *a= rgs) +{ + struct scmi_tlm_config cfg =3D st->cfg; + bool val; + int ret; + + if (args->cnt < 2 || !args->opts) + return -1; + + val =3D strncmp(args->opts[1], "on", strlen(args->opts[1])) =3D=3D 0; + + cfg.enable =3D !!val; + cfg.flags =3D 0; + + fprintf(stdout, "\n---%sabling Telemetry\n", val ? "En" : "Dis"); + ret =3D ioctl(st->fd, SCMI_TLM_SET_CFG, &cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SET_CFG)); + return -errno; + } + + return 0; +} + +static int configure_update(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_config cfg =3D st->cfg; + unsigned long val; + int ret; + + if (args->cnt < 2 || !args->opts) + return -1; + + val =3D strtoul(args->opts[1], NULL, 0); + if (val =3D=3D ULONG_MAX || errno =3D=3D EINVAL) + return -1; + + cfg.flags =3D 0; + if (!val) { + cfg.enable =3D val; + fprintf(stdout, "\n---Disabling Telemetry\n"); + } else { + cfg.enable =3D !!val; + cfg.active.secs =3D val; + cfg.active.exp =3D -3; + fprintf(stdout, "\n--- Setting update interval to %lu\n", val); + } + + ret =3D ioctl(st->fd, SCMI_TLM_SET_CFG, &cfg); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_SET_CFG)); + return -errno; + } + + return 0; +} + +static int cmd_configure(struct tlm_state *st, struct parsed_args *args) +{ + if (!args->cnt || !args->opts) + return -1; + + if (!strncmp(args->opts[0], "all_des", strlen(args->opts[0]))) + return configure_all_des(st, args); + if (!strncmp(args->opts[0], "tlm", strlen(args->opts[0]))) + return configure_telemetry(st, args); + if (!strncmp(args->opts[0], "update", strlen(args->opts[0]))) + return configure_update(st, args); + if (!strncmp(args->opts[0], "de", strlen(args->opts[0]))) + return configure_de(st, args); + if (!strncmp(args->opts[0], "group", strlen(args->opts[0]))) + return configure_group(st, args); + + return -1; +} + +static inline void cmd_full_scan_and_dump(struct tlm_state *st) +{ + cmd_dump_state(st, NULL); + cmd_dump_des(st); + cmd_dump_groups(st); +} + +static int cmd_uuids(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_uuid *uuids; + size_t u_sz; + int ret; + + batch.item_sz =3D sizeof(*uuids); + /* Get size at first */ + ret =3D ioctl(st->fd, SCMI_TLM_GET_UUID_LIST, &batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_UUID_LIST)); + return -errno; + } + + u_sz =3D sizeof(*uuids) * batch.num_items; + uuids =3D malloc(u_sz); + if (!uuids) + return -ENOMEM; + + bzero(uuids, u_sz); + batch.items =3D (uint64_t)uuids; + ret =3D ioctl(st->fd, SCMI_TLM_GET_UUID_LIST, &batch); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_GET_UUID_LIST)); + return -errno; + } + + fprintf(stdout, "--- [GET] -> NUM_UUIDS: %u\n", batch.num_items); + for (int i =3D 0; i < batch.num_items; i++) { + print_uuid(" UUID: ", + &(((struct scmi_tlm_uuid *)(batch.items))[i])); + fprintf(stdout, "\n"); + } + + return 0; +} + +static int cmd_monitor(struct tlm_state *st, struct parsed_args *args) +{ + struct scmi_tlm_event evt =3D {}; + struct pollfd fds[2]; + sigset_t mask; + int ret, efd, sfd; + + efd =3D eventfd(0, EFD_CLOEXEC); + if (efd < 0) + return -errno; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGQUIT); + ret =3D sigprocmask(SIG_BLOCK, &mask, NULL); + if (ret < 0) { + close(efd); + return -errno; + } + + sfd =3D signalfd(-1, &mask, 0); + if (sfd < 0) { + close(efd); + return -errno; + } + + fds[0].fd =3D sfd; + fds[0].events =3D POLLIN; + + evt.type =3D SCMI_TLM_EVT_GENERATION; + evt.efd =3D efd; + fprintf(stdout, "=3D=3D>> Registering for events of type %u\n", evt.type); + ret =3D ioctl(st->fd, SCMI_TLM_EVENT_SUBSCRIBE, &evt); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_EVENT_SUBSCRIBE)); + return -errno; + } + + fds[1].fd =3D evt.efd; + fds[1].events =3D POLLIN; + + if (args->cnt) { + fprintf(stdout, "=3D=3D>> Closing FD on TLM device...kills eventfd\n"); + close(st->fd); + } + + fprintf(stdout, "=3D=3D>> Waiting for events of type %u on cookie:%u\n", + evt.type, evt.cookie); + while (1) { + ret =3D poll(fds, 2, -1); + if (ret < 0) + return ret; + + /* Break on SIGINT */ + if (fds[0].revents & POLLIN) { + struct signalfd_siginfo fdsi; + + read(fds[0].fd, &fdsi, sizeof(fdsi)); + if (fdsi.ssi_signo =3D=3D SIGINT) + fprintf(stderr, "Got SIGINT\n"); + else + fprintf(stderr, "Got signal\n"); + break; + } + + if (fds[1].revents & POLLIN) { + uint64_t val =3D 0; + + read(fds[1].fd, &val, sizeof(val)); + fprintf(stdout, "=3D=3D>> EVENT READ on EFD:%d - VAL:%lu!\n", + evt.efd, val); + } + } + + if (!args->cnt) { + fprintf(stdout, "=3D=3D>> DE-Registering for events of type %u on cookie= :%u\n", + evt.type, evt.cookie); + ret =3D ioctl(st->fd, SCMI_TLM_EVENT_SUBSCRIBE, &evt); + if (ret < 0) { + perror(IOCTL_ERR_STR(SCMI_TLM_EVENT_SUBSCRIBE)); + return -errno; + } + } + + close(sfd); + close(efd); + + return 0; +} + +static int query_intervals(struct tlm_state *st) +{ + struct scmi_tlm_intervals tlm_ivs =3D {}; + int ret; + + /* Global interval */ + ret =3D ioctl(st->fd, SCMI_TLM_GET_INTRVS, &tlm_ivs); + if (ret < 0) { + perror("SCMI_TLM_GET_INTRVS"); + return -errno; + } + + fprintf(stdout, "--- [QUERY]:[SCMI_TLM_GET_INTRVS] -> NUM_ITEMS:%u\n", + tlm_ivs.num_intervals); + + for (int i =3D 0; i < st->num_groups; i++) { + struct scmi_tlm_intervals tlm_ivs =3D {}; + + tlm_ivs.grp_id =3D st->grps[i].info->grp_id; + tlm_ivs.flags =3D SCMI_TLM_INTERV_GROUP; + ret =3D ioctl(st->fd, SCMI_TLM_GET_INTRVS, &tlm_ivs); + if (ret < 0) { + perror("SCMI_TLM_GET_INTRVS"); + return -errno; + } + + fprintf(stdout, + "--- [QUERY][GRP%u]:[SCMI_TLM_GET_INTRVS] -> NUM_ITEMS:%u\n", + tlm_ivs.grp_id, tlm_ivs.num_intervals); + } + + return 0; +} + +static int query_samples(struct tlm_state *st, unsigned long ioctl_op, + const char *tag) +{ + struct scmi_tlm_data_read data =3D {}; + int ret; + + /* Global interval */ + ret =3D ioctl(st->fd, ioctl_op, &data); + if (ret < 0) { + perror(tag); + return -errno; + } + + fprintf(stdout, "--- [QUERY]:[%s] -> NUM_ITEMS:%u\n", + tag, data.num_samples); + + for (int i =3D 0; i < st->num_groups; i++) { + struct scmi_tlm_data_read data =3D {}; + + data.grp_id =3D st->grps[i].info->grp_id; + data.flags =3D SCMI_TLM_READ_GROUP; + ret =3D ioctl(st->fd, ioctl_op, &data); + if (ret < 0) { + perror(tag); + return -errno; + } + + fprintf(stdout, + "--- [QUERY][GRP%u]:[%s] -> NUM_ITEMS:%u\n", + data.grp_id, tag, data.num_samples); + } + + return 0; +} + +static void cmd_query_ioctls(struct tlm_state *st) +{ + int ret; + + ret =3D query_items(st->fd, SCMI_TLM_GET_DE_LIST, + sizeof(struct scmi_tlm_de_info), + "SCMI_TLM_GET_DE_LIST"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_GET_GRP_LIST, + sizeof(struct scmi_tlm_grp_info), + "SCMI_TLM_GET_GRP_LIST"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_GET_SHMTI_LIST, + sizeof(struct scmi_tlm_shmti_info), + "SCMI_TLM_GET_SHMTI_LIST"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_GET_UUID_LIST, + sizeof(struct scmi_tlm_uuid), + "SCMI_TLM_GET_UUID_LIST"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_GET_DE_CFG, + sizeof(struct scmi_tlm_de_config), + "SCMI_TLM_GET_DE_CFG"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_SET_DE_CFG, + sizeof(struct scmi_tlm_de_config), + "SCMI_TLM_SET_DE_CFG"); + if (ret) + return; + + ret =3D query_items(st->fd, SCMI_TLM_BATCH_READ, + sizeof(struct scmi_tlm_de_sample), + "SCMI_TLM_BATCH_READ"); + if (ret) + return; + + ret =3D query_intervals(st); + if (ret) + return; + + ret =3D query_samples(st, SCMI_TLM_BULK_READ, "SCMI_TLM_BULK_READ"); + if (ret) + return; + + ret =3D query_samples(st, SCMI_TLM_SINGLE_READ, "SCMI_TLM_SINGLE_READ"); + if (ret) + return; +} + +int main(int argc, char **argv) +{ + struct parsed_args args =3D {}; + struct tlm_state *st; + int ret; + + ret =3D parse_options(argc, argv, &args); + if (ret < 0) { + usage(argv[0]); + return EXIT_FAILURE; + } + + st =3D open_session(args.instance); + if (!st) + return EXIT_FAILURE; + + ret =3D gather_tlm_state(st); + if (ret) + return EXIT_FAILURE; + + if (!args.cmd) { + cmd_full_scan_and_dump(st); + } else if (!strncmp(args.cmd, "info", strlen(args.cmd))) { + cmd_dump_state(st, &args); + } else if (!strncmp(args.cmd, "uuids", strlen(args.cmd))) { + cmd_uuids(st, &args); + } else if (!strncmp(args.cmd, "monitor", strlen(args.cmd))) { + cmd_monitor(st, &args); + } else if (!strncmp(args.cmd, "shmti", strlen(args.cmd))) { + cmd_shmti_ops(st, &args); + } else if (!strncmp(args.cmd, "des", strlen(args.cmd))) { + cmd_dump_des(st); + } else if (!strncmp(args.cmd, "groups", strlen(args.cmd))) { + cmd_dump_groups(st); + } else if (!strncmp(args.cmd, "cfg", strlen(args.cmd))) { + cmd_configure(st, &args); + } else if (!strncmp(args.cmd, "single", strlen(args.cmd))) { + cmd_single_read(st); + } else if (!strncmp(args.cmd, "bulk", strlen(args.cmd))) { + cmd_bulk_read(st); + } else if (!strncmp(args.cmd, "batch", strlen(args.cmd))) { + cmd_batch_read(st, &args); + } else if (!strncmp(args.cmd, "reset", strlen(args.cmd))) { + cmd_tlm_reset(st); + } else if (!strncmp(args.cmd, "query", strlen(args.cmd))) { + cmd_query_ioctls(st); + } else { + fprintf(stderr, "Unknown command '%s'\n", args.cmd); + usage(argv[0]); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} --=20 2.54.0 From nobody Thu Sep 24 20:34:09 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9DD993F107B; Sun, 20 Sep 2026 09:21:58 +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=1789896123; cv=none; b=izWggEc8K02zjaVLzZGwlfVplxyK5Uw3JY/ClgwTOQuQtZHibsZ720V9FKxRucXUVjbou7tljrFnxZyvbltZS/TAM4xa98upUXjmBZPLorvbK1U/p6M2lwBm7Ov9/4KpXNTJy0lUYVSQYLLCzSHdhEQ/3q3L4ANI/XjOFExTKUk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789896123; c=relaxed/simple; bh=47c2ovhdgbKoZwZCmXgUS/qG5xlW+XVrDP5iVO6o1do=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c+qA5hrfxYFK4/dwgoQCYgZ2aYjw1BW/r4pC2ZP0h6r1XslGxKKu9wpDoO5wOK2Lp96PfW7pw4HuEWzK3s/saOurYhrdGpXE9BrzC/XL1HXhnEGCHRPc1Hkot8L/fqnSOv/PxVcBwaTfvJQcz3qU9GO3iL99TpcEQIfMpWtwu3Y= 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=KASnYR0d; 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="KASnYR0d" 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 6D39C175A; Sun, 20 Sep 2026 02:21:54 -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 0E9C53F59E; Sun, 20 Sep 2026 02:21:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789896117; bh=47c2ovhdgbKoZwZCmXgUS/qG5xlW+XVrDP5iVO6o1do=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KASnYR0dkDC2vGjOQC4Mepq04uzHtNtNPv9994yZ/1RpFKUeSo+w8sNoCiCGdufF+ t9rRc9ZR1NjiKje04iXBhcWkvEwRSqXRMfKB2cuxBqFAeBr6Ye7Oc+b6lTMfqMMOAM xBXwG4brw1/y00mVcE7VfaL8GYzD68l2y+RTEsZE= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@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, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi Subject: [PATCH v12 25/25] [RFC] kselftest/arm64: Add SCMI Telemetry UAPI compliance testcases Date: Sun, 20 Sep 2026 10:19:28 +0100 Message-ID: <20260920091928.2014972-26-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260920091928.2014972-1-cristian.marussi@arm.com> References: <20260920091928.2014972-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" From: Philip Radford Add support to test SCMI Telemetry UAPI compliance testcases. Co-developed-by: Cristian Marussi Signed-off-by: Philip Radford Signed-off-by: Cristian Marussi --- tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/scmi/Makefile | 12 + tools/testing/selftests/arm64/scmi/config | 2 + .../arm64/scmi/test_scmi_telemetry_uapi.c | 1040 +++++++++++++++++ 4 files changed, 1055 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/scmi/Makefile create mode 100644 tools/testing/selftests/arm64/scmi/config create mode 100644 tools/testing/selftests/arm64/scmi/test_scmi_telemetry_= uapi.c diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftes= ts/arm64/Makefile index e456f3b62fa1..f3beae2ae164 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -4,7 +4,7 @@ ARCH ?=3D $(shell uname -m 2>/dev/null || echo not) =20 ifneq (,$(filter $(ARCH),aarch64 arm64)) -ARM64_SUBTARGETS ?=3D tags signal pauth fp mte bti abi gcs +ARM64_SUBTARGETS ?=3D tags signal pauth fp mte bti abi gcs scmi else ARM64_SUBTARGETS :=3D endif diff --git a/tools/testing/selftests/arm64/scmi/Makefile b/tools/testing/se= lftests/arm64/scmi/Makefile new file mode 100644 index 000000000000..3d9576c7a3e8 --- /dev/null +++ b/tools/testing/selftests/arm64/scmi/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2026 ARM Limited + +# Additional include paths needed by kselftest.h and local headers +CFLAGS +=3D -std=3Dgnu99 -I. +LDFLAGS +=3D -static + +# Generated binaries to be installed by top KSFT script +TEST_GEN_PROGS :=3D test_scmi_telemetry_uapi + +# Include KSFT lib.mk. +include ../../lib.mk diff --git a/tools/testing/selftests/arm64/scmi/config b/tools/testing/self= tests/arm64/scmi/config new file mode 100644 index 000000000000..63cd7e67ab55 --- /dev/null +++ b/tools/testing/selftests/arm64/scmi/config @@ -0,0 +1,2 @@ +CONFIG_ARM_SCMI_SYSTEM_TELEMETRY=3Dy + diff --git a/tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c = b/tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c new file mode 100644 index 000000000000..87f4d0c1e98f --- /dev/null +++ b/tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c @@ -0,0 +1,1040 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 ARM Limited + +#include +#include +#include +#include +#include + +#include "kselftest_harness.h" + +#include <../../../../../include/uapi/linux/scmi.h> + +#define TLM_0 "tlm_0" + +struct scmi_ksft_session { + bool initialized; + struct scmi_tlm_abi_info info; + struct scmi_tlm_config *original_cfg; + struct scmi_tlm_intervals available; + struct scmi_tlm_update_interval *intervals; + struct scmi_tlm_de_info *de_list; + unsigned int num_des; + +} ksft_session; + +static int scmi_tlm_batch_init(struct scmi_tlm_batch *batch, + unsigned int num_items, + size_t item_sz, + bool with_states) +{ + void *items =3D NULL; + int *states =3D NULL; + + bzero(batch, sizeof(*batch)); + + if (num_items) { + items =3D malloc(item_sz * num_items); + if (!items) + return -1; + + bzero(items, item_sz * num_items); + } + + if (with_states && num_items) { + states =3D malloc(sizeof(*states) * num_items); + if (!states) { + free(items); + return -1; + } + + bzero(states, sizeof(*states) * num_items); + } + + batch->num_items =3D num_items; + batch->item_sz =3D item_sz; + batch->items =3D (__u64)items; + batch->states =3D (__u64)states; + + return 0; +} + +static void scmi_tlm_batch_free(struct scmi_tlm_batch *batch) +{ + free((void *)batch->items); + free((void *)batch->states); + bzero(batch, sizeof(*batch)); +} + +static __attribute__((constructor)) void scmi_tlm_initialize(void) +{ + struct scmi_tlm_batch batch =3D {}; + int fd, ret; + + fd =3D open("/dev/scmi/" TLM_0, O_RDWR); + if (fd < 0) { + fprintf(stderr, "INIT: device not available\n"); + return; + } + + ksft_session.info.size =3D sizeof(struct scmi_tlm_abi_info); + ret =3D ioctl(fd, SCMI_TLM_GET_ABI_INFO, &ksft_session.info); + if (ret) { + fprintf(stderr, "INIT: failed to get ABI info\n"); + goto out; + } + + if (ksft_session.info.abi_version !=3D SCMI_TLM_ABI_VERSION_V1) { + fprintf(stderr, "INIT: ABI Version %u NOT supported", + ksft_session.info.abi_version); + goto out; + } + + if (ksft_session.info.num_des) { + ret =3D scmi_tlm_batch_init(&batch, ksft_session.info.num_des, + sizeof(*ksft_session.de_list), false); + if (!ret) { + ret =3D ioctl(fd, SCMI_TLM_GET_DE_LIST, &batch); + if (!ret) { + ksft_session.de_list =3D (struct scmi_tlm_de_info *)batch.items; + ksft_session.num_des =3D batch.num_items; + } else { + scmi_tlm_batch_free(&batch); + } + } + } + + if (ksft_session.info.num_intervals) { + int num_intervals =3D ksft_session.info.num_intervals; + struct scmi_tlm_update_interval *intervals; + + intervals =3D malloc(sizeof(*intervals) * num_intervals); + if (!intervals) { + fprintf(stderr, "INIT: Failed to allocate intervals"); + goto out; + } + + bzero(intervals, sizeof(*intervals) * num_intervals); + + ksft_session.intervals =3D intervals; + ksft_session.available.intervals =3D (__u64)intervals; + ksft_session.available.num_intervals =3D num_intervals; + + ret =3D ioctl(fd, SCMI_TLM_GET_INTRVS, &ksft_session.available); + if (ret) { + fprintf(stderr, "INIT: Failed to retrieve intervals"); + free(intervals); + goto out; + } + } + + fprintf(stdout, "INIT: Found ABI version %u\n", + ksft_session.info.abi_version); + fprintf(stdout, "INIT: Discovered %u global update intervals\n", + ksft_session.available.num_intervals); + + ksft_session.original_cfg =3D malloc(sizeof(*ksft_session.original_cfg)); + if (ksft_session.original_cfg) { + bzero(ksft_session.original_cfg, sizeof(*ksft_session.original_cfg)); + + ret =3D ioctl(fd, SCMI_TLM_GET_CFG, ksft_session.original_cfg); + if (ret) { + free(ksft_session.original_cfg); + ksft_session.original_cfg =3D NULL; + } + } + + ksft_session.initialized =3D true; + +out: + close(fd); +} + +TEST(scmi_tlm_config_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_config)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_config, enable)); + ASSERT_EQ(1, offsetof(struct scmi_tlm_config, t_enable)); + ASSERT_EQ(2, offsetof(struct scmi_tlm_config, flags)); + ASSERT_EQ(3, offsetof(struct scmi_tlm_config, pad)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_config, grp_id)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_config, active)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_config, reserved)); +} + +TEST(scmi_tlm_abi_info_struct_layout) { + ASSERT_EQ(56, sizeof(struct scmi_tlm_abi_info)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_abi_info, size)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_abi_info, abi_version)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_abi_info, abi_features)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_abi_info, primary_de_impl_version)= ); + ASSERT_EQ(28, offsetof(struct scmi_tlm_abi_info, num_des)); + ASSERT_EQ(32, offsetof(struct scmi_tlm_abi_info, num_groups)); + ASSERT_EQ(36, offsetof(struct scmi_tlm_abi_info, num_intervals)); + ASSERT_EQ(40, offsetof(struct scmi_tlm_abi_info, num_shmtis)); + ASSERT_EQ(44, offsetof(struct scmi_tlm_abi_info, features)); + ASSERT_EQ(48, offsetof(struct scmi_tlm_abi_info, reserved)); +} + +TEST(scmi_tlm_update_interval_struct_layout) { + ASSERT_EQ(8, sizeof(struct scmi_tlm_update_interval)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_update_interval, secs)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_update_interval, exp)); +} + +TEST(scmi_tlm_intervals_struct_layout) { + ASSERT_EQ(32, sizeof(struct scmi_tlm_intervals)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_intervals, grp_id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_intervals, flags)); + ASSERT_EQ(5, offsetof(struct scmi_tlm_intervals, pad)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_intervals, num_intervals)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_intervals, pad2)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_intervals, reserved)); + ASSERT_EQ(24, offsetof(struct scmi_tlm_intervals, intervals)); +} + +TEST(scmi_tlm_de_config_struct_layout) { + ASSERT_EQ(48, sizeof(struct scmi_tlm_de_config)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_de_config, id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_de_config, enable)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_de_config, t_enable)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_de_config, sid)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_de_config, offset)); + ASSERT_EQ(20, offsetof(struct scmi_tlm_de_config, pad)); + ASSERT_EQ(24, offsetof(struct scmi_tlm_de_config, uuid)); + ASSERT_EQ(40, offsetof(struct scmi_tlm_de_config, reserved)); +} + +TEST(scmi_tlm_de_info_struct_layout) { + ASSERT_EQ(72, sizeof(struct scmi_tlm_de_info)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_de_info, id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_de_info, grp_id)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_de_info, data_sz)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_de_info, type)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_de_info, unit)); + ASSERT_EQ(20, offsetof(struct scmi_tlm_de_info, unit_exp)); + ASSERT_EQ(24, offsetof(struct scmi_tlm_de_info, ts_rate)); + ASSERT_EQ(28, offsetof(struct scmi_tlm_de_info, instance_id)); + ASSERT_EQ(32, offsetof(struct scmi_tlm_de_info, compo_instance_id)); + ASSERT_EQ(36, offsetof(struct scmi_tlm_de_info, compo_type)); + ASSERT_EQ(40, offsetof(struct scmi_tlm_de_info, persistent)); + ASSERT_EQ(41, offsetof(struct scmi_tlm_de_info, flags)); + ASSERT_EQ(42, offsetof(struct scmi_tlm_de_info, pad)); + ASSERT_EQ(44, offsetof(struct scmi_tlm_de_info, pad2)); + ASSERT_EQ(48, offsetof(struct scmi_tlm_de_info, name)); + ASSERT_EQ(64, offsetof(struct scmi_tlm_de_info, reserved)); +} + +TEST(scmi_tlm_de_sample_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_de_sample)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_de_sample, id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_de_sample, pad)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_de_sample, tstamp)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_de_sample, val)); +} + +TEST(scmi_tlm_data_read_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_data_read)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_data_read, grp_id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_data_read, flags)); + ASSERT_EQ(5, offsetof(struct scmi_tlm_data_read, pad)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_data_read, pad2)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_data_read, num_samples)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_data_read, samples)); +} + +TEST(scmi_tlm_batch_struct_layout) { + ASSERT_EQ(32, sizeof(struct scmi_tlm_batch)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_batch, num_items)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_batch, item_sz)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_batch, reserved)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_batch, states)); + ASSERT_EQ(24, offsetof(struct scmi_tlm_batch, items)); +} + +TEST(scmi_tlm_grp_info_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_grp_info)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_grp_info, grp_id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_grp_info, num_des)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_grp_info, num_intervals)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_grp_info, pad)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_grp_info, reserved)); +} + +TEST(scmi_tlm_grp_desc_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_grp_desc)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_grp_desc, grp_id)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_grp_desc, num_des)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_grp_desc, composing_des)); +} + +TEST(scmi_tlm_shmti_info_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_shmti_info)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_shmti_info, sid)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_shmti_info, fd)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_shmti_info, len)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_shmti_info, offset)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_shmti_info, reserved)); +} + +TEST(scmi_tlm_uuid_struct_layout) { + ASSERT_EQ(16, sizeof(struct scmi_tlm_uuid)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_uuid, bytes)); +} + +TEST(scmi_tlm_event_struct_layout) { + ASSERT_EQ(24, sizeof(struct scmi_tlm_event)); + ASSERT_EQ(0, offsetof(struct scmi_tlm_event, type)); + ASSERT_EQ(4, offsetof(struct scmi_tlm_event, efd)); + ASSERT_EQ(8, offsetof(struct scmi_tlm_event, cookie)); + ASSERT_EQ(12, offsetof(struct scmi_tlm_event, pad)); + ASSERT_EQ(16, offsetof(struct scmi_tlm_event, reserved)); +} + +FIXTURE(stlm) { + int fd; +}; + +FIXTURE_SETUP(stlm) { + if (!ksft_session.initialized) + SKIP(return, "initialization failed"); + + self->fd =3D open("/dev/scmi/" TLM_0, O_RDWR); + if (self->fd < 0) + SKIP(return, "device not available"); + + TH_LOG("Testing ABI Version %u on device %s", + ksft_session.info.abi_version, TLM_0); +} + +FIXTURE_TEARDOWN(stlm) { + close(self->fd); +} + +TEST_F(stlm, tlm_get_cfg) { + struct scmi_tlm_config cfg =3D {}; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + ASSERT_EQ(0, cfg.pad); + ASSERT_EQ(0, cfg.reserved); + ASSERT_EQ(0, cfg.flags & ~SCMI_TLM_CONFIG_FLAGS); + + TH_LOG("SCMI_TLM_GET_CFG - ena:%u t_ena:%u secs:%u exp:%d", + cfg.enable, cfg.t_enable, cfg.active.secs, cfg.active.exp); + + for (int i =3D 0; i < ksft_session.info.num_groups; i++) { + cfg.flags =3D SCMI_TLM_CONFIG_GROUP; + cfg.grp_id =3D i; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + ASSERT_EQ(0, cfg.pad); + ASSERT_EQ(0, cfg.reserved); + ASSERT_EQ(SCMI_TLM_CONFIG_GROUP, cfg.flags & SCMI_TLM_CONFIG_FLAGS); + ASSERT_EQ(i, cfg.grp_id); + + TH_LOG("SCMI_TLM_GET_CFG - ena:%u t_ena:%u grp_id:%u secs:%u exp:%d", + cfg.enable, cfg.t_enable, cfg.grp_id, cfg.active.secs, cfg.active= .exp); + } + + /* Invalid GRP ID should be ignored when proper flags missing */ + cfg.grp_id =3D ksft_session.info.num_groups; + cfg.flags =3D 0x0; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + ASSERT_EQ(0, cfg.pad); + ASSERT_EQ(0, cfg.reserved); +} + +TEST_F(stlm, tlm_get_cfg_bad_group) { + struct scmi_tlm_config cfg =3D {}; + + cfg.flags =3D SCMI_TLM_CONFIG_GROUP; + cfg.grp_id =3D ksft_session.info.num_groups; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + ASSERT_EQ(0, cfg.pad); + ASSERT_EQ(0, cfg.reserved); + ASSERT_EQ(0, cfg.flags & ~SCMI_TLM_CONFIG_FLAGS); +} + +TEST_F(stlm, tlm_get_cfg_null_ptr_rejected) { + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_CFG, NULL)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_get_cfg_invalid_request) { + struct scmi_tlm_config cfg =3D { }; + + cfg.pad =3D 0x1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + EXPECT_EQ(EINVAL, errno); + + cfg.pad =3D 0x0; + cfg.reserved =3D 0x1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + EXPECT_EQ(EINVAL, errno); + + cfg.reserved =3D 0x0; + cfg.flags =3D ~SCMI_TLM_CONFIG_FLAGS; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_CFG, &cfg)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_set_cfg) { + struct scmi_tlm_config old_cfg =3D {}; + struct scmi_tlm_config new_cfg =3D {}; + struct scmi_tlm_config check_cfg =3D {}; + + if (!ksft_session.original_cfg) + SKIP(return, "original config not available"); + + old_cfg =3D *ksft_session.original_cfg; + + new_cfg =3D old_cfg; + new_cfg.enable =3D !old_cfg.enable; + + if (ksft_session.info.num_intervals > 1) { + int num_intervals =3D ksft_session.info.num_intervals; + struct scmi_tlm_update_interval *intervals; + + intervals =3D (struct scmi_tlm_update_interval *)ksft_session.available.= intervals; + for (int i =3D 0; i < num_intervals; i++) { + if (intervals[i].secs =3D=3D old_cfg.active.secs && + intervals[i].exp =3D=3D old_cfg.active.exp) + continue; + + /* pick another valid interval */ + new_cfg.active.secs =3D intervals[i].secs; + new_cfg.active.exp =3D intervals[i].exp; + break; + } + } + + TH_LOG("SCMI_TLM_SET_CFG - ena:%u t_ena:%u secs:%u exp:%d", + new_cfg.enable, new_cfg.t_enable, new_cfg.active.secs, new_cfg.act= ive.exp); + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_SET_CFG, &new_cfg)); + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_CFG, &check_cfg)); + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_SET_CFG, &old_cfg)); + + ASSERT_EQ(new_cfg.enable, check_cfg.enable); + ASSERT_EQ(new_cfg.active.secs, check_cfg.active.secs); + ASSERT_EQ(new_cfg.active.exp, check_cfg.active.exp); + ASSERT_EQ(0, check_cfg.pad); + ASSERT_EQ(0, check_cfg.reserved); + +} + +TEST_F(stlm, tlm_get_intrvs) { + struct scmi_tlm_update_interval *intervals; + struct scmi_tlm_intervals ivs =3D {}; + int num_intervals =3D ksft_session.info.num_intervals; + + if (!num_intervals) + SKIP(return, "No update intervals available"); + + intervals =3D malloc(sizeof(*intervals) * num_intervals); + ASSERT_NE(NULL, intervals); + bzero(intervals, sizeof(*intervals) * num_intervals); + + ivs.num_intervals =3D num_intervals; + ivs.intervals =3D (__u64)intervals; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); + + ASSERT_EQ(num_intervals, ivs.num_intervals); + ASSERT_EQ(0, ivs.pad[0]); + ASSERT_EQ(0, ivs.pad[1]); + ASSERT_EQ(0, ivs.pad[2]); + ASSERT_EQ(0, ivs.pad2); + ASSERT_EQ(0, ivs.reserved); + ASSERT_EQ(0, ivs.flags & ~SCMI_TLM_INTERV_FLAGS); + + for (int i =3D 0; i < num_intervals; i++) { + TH_LOG("SCMI_TLM_GET_INTRVS - [%d] secs:%u exp:%d", i, + intervals[i].secs, intervals[i].exp); + } + + free(intervals); +} + +TEST_F(stlm, tlm_get_intrvs_invalid_request) { + struct scmi_tlm_update_interval interval =3D {}; + struct scmi_tlm_intervals ivs =3D {}; + + ivs.num_intervals =3D 1; + ivs.intervals =3D (__u64)&interval; + ivs.pad[0] =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); + EXPECT_EQ(EINVAL, errno); + + bzero(&ivs, sizeof(ivs)); + ivs.num_intervals =3D 1; + ivs.intervals =3D (__u64)&interval; + ivs.pad2 =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); + EXPECT_EQ(EINVAL, errno); + + bzero(&ivs, sizeof(ivs)); + ivs.num_intervals =3D 1; + ivs.intervals =3D (__u64)&interval; + ivs.reserved =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); + EXPECT_EQ(EINVAL, errno); + + bzero(&ivs, sizeof(ivs)); + ivs.num_intervals =3D 1; + ivs.intervals =3D (__u64)&interval; + ivs.flags =3D ~SCMI_TLM_INTERV_FLAGS; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); +} + +TEST_F(stlm, tlm_get_intrvs_oversized) { + struct scmi_tlm_update_interval interval =3D {}; + struct scmi_tlm_intervals ivs =3D {}; + + ivs.num_intervals =3D ksft_session.info.num_intervals + 1; + ivs.intervals =3D (__u64)&interval; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_INTRVS, &ivs)); + EXPECT_EQ(ENOSPC, errno); +} + +TEST_F(stlm, tlm_get_de_list) +{ + struct scmi_tlm_de_info *des; + struct scmi_tlm_batch batch =3D {}; + + if (!ksft_session.info.num_des) + SKIP(return, "no DEs available"); + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, ksft_session.info.num_des, + sizeof(*des), false)); + + des =3D (struct scmi_tlm_de_info *)batch.items; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + + ASSERT_LE(batch.num_items, ksft_session.info.num_des); + + for (int i =3D 0; i < batch.num_items; i++) { + ASSERT_EQ(0, des[i].reserved); + ASSERT_EQ(0, des[i].pad[0]); + ASSERT_EQ(0, des[i].pad[1]); + ASSERT_EQ(0, des[i].pad2); + ASSERT_EQ(0, des[i].flags & ~SCMI_TLM_DEINFO_FLAGS); + + TH_LOG("SCMI_TLM_GET_DE_LIST - [%d] id:0x%x name:%s", i, des[i].id, + des[i].name); + } + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_get_de_list_zero_size_query) +{ + struct scmi_tlm_batch batch =3D {}; + + batch.num_items =3D 0; + batch.item_sz =3D sizeof(struct scmi_tlm_de_info); + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + ASSERT_EQ(ksft_session.info.num_des, batch.num_items); + ASSERT_EQ(sizeof(struct scmi_tlm_de_info), batch.item_sz); + ASSERT_EQ(0, batch.reserved); + ASSERT_EQ(0, batch.items); +} + +TEST_F(stlm, tlm_get_de_list_zero_size_query_bad_item_sz) +{ + struct scmi_tlm_batch batch =3D {}; + + batch.num_items =3D 0; + batch.item_sz =3D sizeof(struct scmi_tlm_de_info) + 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_get_de_list_invalid_request) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_info de =3D {}; + + batch.num_items =3D 1; + batch.item_sz =3D sizeof(de) + 1; + batch.items =3D (__u64)&de; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D 1; + batch.item_sz =3D sizeof(de); + batch.reserved =3D 1; + batch.items =3D (__u64)&de; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D ksft_session.info.num_des + 1; + batch.item_sz =3D sizeof(de); + batch.items =3D (__u64)&de; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_LIST, &batch)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_get_de_info) +{ + struct scmi_tlm_de_info info =3D {}; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + info.id =3D ksft_session.de_list[0].id; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_DE_INFO, &info)); + ASSERT_EQ(0, info.reserved); + ASSERT_EQ(0, info.pad[0]); + ASSERT_EQ(0, info.pad[1]); + ASSERT_EQ(0, info.pad2); + ASSERT_EQ(0, info.flags & ~SCMI_TLM_DEINFO_FLAGS); + + TH_LOG("SCMI_TLM_GET_DE_INFO - id:0x%x name:%s data_sz:%u", + info.id, info.name, info.data_sz); +} + +TEST_F(stlm, tlm_get_de_info_invalid_request) +{ + struct scmi_tlm_de_info info =3D {}; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + info.id =3D ksft_session.de_list[0].id; + info.flags =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_INFO, &info)); + EXPECT_EQ(EINVAL, errno); + + bzero(&info, sizeof(info)); + info.id =3D ksft_session.de_list[0].id; + info.pad[0] =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_INFO, &info)); + EXPECT_EQ(EINVAL, errno); + + bzero(&info, sizeof(info)); + info.id =3D ksft_session.de_list[0].id; + info.reserved =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_INFO, &info)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_get_de_info_bad_id) +{ + struct scmi_tlm_de_info info =3D {}; + + info.id =3D 0xffffffff; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_INFO, &info)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_get_de_cfg) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_config *cfg; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*cfg), false)); + + cfg =3D (struct scmi_tlm_de_config *)batch.items; + cfg[0].id =3D ksft_session.de_list[0].id; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + ASSERT_EQ(1, batch.num_items); + ASSERT_EQ(ksft_session.de_list[0].id, cfg[0].id); + ASSERT_EQ(0, cfg[0].reserved); + + TH_LOG("SCMI_TLM_GET_DE_CFG - id:0x%x ena:%u sid:%u offset:%u", + cfg[0].id, cfg[0].enable, cfg[0].sid, + cfg[0].offset); + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_get_de_cfg_invalid_request) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_config cfg =3D {}; + + batch.num_items =3D 1; + batch.item_sz =3D sizeof(cfg) + 1; + batch.items =3D (__u64)&cfg; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D ksft_session.info.num_des + 1; + batch.item_sz =3D sizeof(cfg); + batch.items =3D (__u64)&cfg; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D 1; + batch.item_sz =3D sizeof(cfg); + batch.items =3D 0; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_get_de_cfg_bad_id) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_config *cfg; + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*cfg), false)); + + cfg =3D (struct scmi_tlm_de_config *)batch.items; + cfg[0].id =3D 0xffffffff; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + EXPECT_EQ(ENODEV, errno); + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_get_de_cfg_bad_id_with_states) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_config *cfg; + int *states; + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*cfg), true)); + + cfg =3D (struct scmi_tlm_de_config *)batch.items; + states =3D (int *)batch.states; + + cfg[0].id =3D 0xffffffff; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_GET_DE_CFG, &batch)); + ASSERT_LT(states[0], 0); + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_de_read) +{ + struct scmi_tlm_de_sample sample =3D {}; + __u32 de_id; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list availalbe"); + + de_id =3D ksft_session.de_list[0].id; + sample.id =3D de_id; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_DE_READ, &sample)); + ASSERT_EQ(de_id, sample.id); + ASSERT_EQ(0, sample.pad); + + TH_LOG("SCMI_TLM_DE_READ - id:0x%x tstamp:%llu val:%llu", sample.id, + sample.tstamp, sample.val); +} + +TEST_F(stlm, tlm_de_read_invalid_request) +{ + struct scmi_tlm_de_sample sample =3D {}; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + sample.id =3D ksft_session.de_list[0].id; + sample.pad =3D 1; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_DE_READ, &sample)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_de_read_null_ptr_rejected) +{ + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_DE_READ, NULL)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_de_read_bad_id) +{ + struct scmi_tlm_de_sample sample =3D {}; + + sample.id =3D 0xffffffff; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_DE_READ, &sample)); + EXPECT_EQ(ENOENT, errno); +} + +TEST_F(stlm, tlm_single_read) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_data_read read =3D {}; + int num_samples =3D ksft_session.num_des; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + samples =3D malloc(sizeof(*samples) * num_samples); + ASSERT_NE(NULL, samples); + bzero(samples, sizeof(*samples) * num_samples); + + read.num_samples =3D num_samples; + read.samples =3D (__u64)samples; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_SINGLE_READ, &read)); + ASSERT_LE(read.num_samples, num_samples); + ASSERT_EQ(0, read.pad[0]); + ASSERT_EQ(0, read.pad[1]); + ASSERT_EQ(0, read.pad[2]); + ASSERT_EQ(0, read.pad2); + ASSERT_EQ(0, read.flags & ~SCMI_TLM_READ_FLAGS); + + for (int i =3D 0; i < read.num_samples; i++) { + ASSERT_EQ(0, samples[i].pad); + + TH_LOG("SCMI_TLM_SINGE_READ =3D [%d] id:0x%x tstamp:%llu val:%llu", i, + samples[i].id, samples[i].tstamp, samples[i].val); + } + + free(samples); +} + +TEST_F(stlm, tlm_single_read_invalid_request) +{ + struct scmi_tlm_de_sample sample =3D {}; + struct scmi_tlm_data_read read =3D {}; + + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.pad[0] =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_SINGLE_READ, &read)); + EXPECT_EQ(EINVAL, errno); + + bzero(&read, sizeof(read)); + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.pad2 =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_SINGLE_READ, &read)); + EXPECT_EQ(EINVAL, errno); + + bzero(&read, sizeof(read)); + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.flags =3D ~SCMI_TLM_READ_FLAGS; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_SINGLE_READ, &read)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_single_read_oversized) +{ + struct scmi_tlm_de_sample sample =3D {}; + struct scmi_tlm_data_read read =3D {}; + + read.num_samples =3D ksft_session.info.num_des + 1; + read.samples =3D (__u64)&sample; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_SINGLE_READ, &read)); + EXPECT_EQ(E2BIG, errno); +} + +TEST_F(stlm, tlm_single_read_null_ptr_rejected) +{ + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_SINGLE_READ, NULL)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_bulk_read) +{ + struct scmi_tlm_de_sample *samples; + struct scmi_tlm_data_read read =3D {}; + int num_samples =3D ksft_session.num_des; + int i; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + samples =3D malloc(sizeof(*samples) * num_samples); + ASSERT_NE(NULL, samples); + bzero(samples, sizeof(*samples) * num_samples); + + read.num_samples =3D num_samples; + read.samples =3D (__u64)samples; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_BULK_READ, &read)); + ASSERT_LE(read.num_samples, num_samples); + ASSERT_EQ(0, read.pad[0]); + ASSERT_EQ(0, read.pad[1]); + ASSERT_EQ(0, read.pad[2]); + ASSERT_EQ(0, read.pad2); + ASSERT_EQ(0, read.flags & ~SCMI_TLM_READ_FLAGS); + + for (i =3D 0; i < read.num_samples; i++) { + ASSERT_EQ(0, samples[i].pad); + + TH_LOG("SCMI_TLM_BULK_READ - [%d] id:0x%x tstamp:%llu val:%llu", i, + samples[i].id, samples[i].tstamp, samples[i].val); + } + + free(samples); +} + +TEST_F(stlm, tlm_bulk_read_invalid_request) +{ + struct scmi_tlm_de_sample sample =3D {}; + struct scmi_tlm_data_read read =3D {}; + + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.pad[0] =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BULK_READ, &read)); + EXPECT_EQ(EINVAL, errno); + + bzero(&read, sizeof(read)); + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.pad2 =3D 1; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BULK_READ, &read)); + EXPECT_EQ(EINVAL, errno); + + bzero(&read, sizeof(read)); + read.num_samples =3D 1; + read.samples =3D (__u64)&sample; + read.flags =3D ~SCMI_TLM_READ_FLAGS; + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BULK_READ, &read)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_bulk_read_oversized) +{ + struct scmi_tlm_de_sample sample =3D {}; + struct scmi_tlm_data_read read =3D {}; + + read.num_samples =3D ksft_session.info.num_des + 1; + read.samples =3D (__u64)&sample; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BULK_READ, &read)); + EXPECT_EQ(E2BIG, errno); +} + +TEST_F(stlm, tlm_bulk_read_null_ptr_rejected) +{ + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BULK_READ, NULL)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_batch_read) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_sample *samples; + + if (!ksft_session.de_list || !ksft_session.num_des) + SKIP(return, "no DE list available"); + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*samples), + false)); + + samples =3D (struct scmi_tlm_de_sample *)batch.items; + samples[0].id =3D ksft_session.de_list[0].id; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + + ASSERT_EQ(1, batch.num_items); + ASSERT_EQ(ksft_session.de_list[0].id, samples[0].id); + ASSERT_EQ(0, samples[0].pad); + + TH_LOG("SCMI_TLM_BATCH_READ - id:0x%x tstamp:%llu val:%llu", samples[0].i= d, + samples[0].tstamp, samples[0].val); + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_batch_read_invalid_request) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_sample sample =3D {}; + + batch.num_items =3D 1; + batch.item_sz =3D sizeof(sample) + 1; + batch.items =3D (__u64)&sample; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D 1; + batch.item_sz =3D sizeof(sample); + batch.reserved =3D 1; + batch.items =3D (__u64)&sample; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + EXPECT_EQ(EINVAL, errno); + + bzero(&batch, sizeof(batch)); + batch.num_items =3D ksft_session.info.num_des + 1; + batch.item_sz =3D sizeof(sample); + batch.items =3D (__u64)&sample; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + EXPECT_EQ(EINVAL, errno); +} + +TEST_F(stlm, tlm_batch_read_bad_items_ptr) +{ + struct scmi_tlm_batch batch =3D {}; + + batch.num_items =3D 1; + batch.item_sz =3D sizeof(struct scmi_tlm_de_sample); + batch.items =3D 0; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + EXPECT_EQ(EFAULT, errno); +} + +TEST_F(stlm, tlm_batch_read_bad_id) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_sample *samples; + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*samples), false)); + + samples =3D (struct scmi_tlm_de_sample *)batch.items; + samples[0].id =3D 0xffffffff; + + ASSERT_EQ(-1, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + EXPECT_EQ(ENOENT, errno); + + scmi_tlm_batch_free(&batch); +} + +TEST_F(stlm, tlm_batch_read_bad_id_with_states) +{ + struct scmi_tlm_batch batch =3D {}; + struct scmi_tlm_de_sample *samples; + int *states; + + ASSERT_EQ(0, scmi_tlm_batch_init(&batch, 1, sizeof(*samples), true)); + + samples =3D (struct scmi_tlm_de_sample *)batch.items; + states =3D (int *)batch.states; + + samples[0].id =3D 0xffffffff; + + ASSERT_EQ(0, ioctl(self->fd, SCMI_TLM_BATCH_READ, &batch)); + ASSERT_EQ(-ENOENT, states[0]); + + scmi_tlm_batch_free(&batch); +} + +TEST_HARNESS_MAIN /* generates main() */ --=20 2.54.0