From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 060214A0921; Wed, 9 Sep 2026 22:04:51 +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=1788991494; cv=none; b=bRwiXpJ0V0rI6n2uusBdxmz6RVPRTZE8Mj+WHyw7PXthUisuKHnwTb5hJor5vgKOTXKg+z4CQCqmuoXq+vd+VaO49PF6g3fqzsYKrHvBwaaA//wz73lxiNfnWbXCgmsEbNzh9Dc9skIwsUvA/csi/TCJFBSLmjvJOpmycJj+ww0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991494; c=relaxed/simple; bh=M/aymMCxdad6o8xZrcE7yr4IgP4ULSikmAxmrW9dYkg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IQdkJj5HvH1ylcoVdphleK6rkPpwJPvFyRBdgaXGKaIivvPUYuGxkNaobkR8N2LDq4gKadpAlj6y3bJB29qnavz9Cj5pddPQrGsg0AF1l6lG1eJWMGByP4NVsr7dHC/9ItJQg1ATWYXJxtbaNSoNr8X9VUZYdPV2jhvrCTIRuOM= 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=fy9mL7b8; 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="fy9mL7b8" 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 6A3081576; Wed, 9 Sep 2026 15:04:47 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EBD653F7D8; Wed, 9 Sep 2026 15:04:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991490; bh=M/aymMCxdad6o8xZrcE7yr4IgP4ULSikmAxmrW9dYkg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fy9mL7b8SXMoStw25MlOMMYqYLALUBODLEhk3PWiXpC1ftRwqhP0cY+kyG71gFj7H uDMqRoYBytAhkSnrH99aNtO4+KcfEgprWQtxQdsh83d+pYODSrHx9Xw44fln+dB9FD kx+uH2Ej5d+P5RS9MU5JXN/e7a2qiUNiISLNX1Gs= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 01/13] powercap: Add enable disable control-type Date: Wed, 9 Sep 2026 22:04:25 +0000 Message-Id: <20260909220437.1254412-2-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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 functionality to disable or enable the Powercap control-type by writing directly into sys/class/powercap/arm-scmi/enabled. Signed-off-by: Philip Radford --- V8->V9 - added check to powercap_set_root_children_enable to make sure it is confi= gurable - added rollback in powercap_control_type_set_enable in case of failure of = one root zone --- drivers/powercap/arm_scmi_powercap.c | 197 ++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 1 deletion(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index ab66e9a3b1e2..96d55921fefc 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -33,13 +33,193 @@ struct scmi_powercap_zone { =20 struct scmi_powercap_root { unsigned int num_zones; + bool enabled; + struct list_head node; struct scmi_powercap_zone *spzones; struct list_head *registered_zones; struct list_head scmi_zones; }; =20 +static LIST_HEAD(scmi_powercap_roots); +static DEFINE_MUTEX(scmi_powercap_roots_lock); + static struct powercap_control_type *scmi_top_pcntrl; =20 +static bool scmi_powercap_is_control_type_child(const struct scmi_powercap= _zone *spz) +{ + return spz->registered && !spz->invalid && + spz->info->parent_id =3D=3D SCMI_POWERCAP_ROOT_ZONE_ID; +} + +static int +scmi_powercap_read_root_children_enable_state(struct scmi_powercap_root *p= r, bool *mode) +{ + struct scmi_powercap_zone *spz; + bool enabled; + int i, ret; + + *mode =3D true; + + for (i =3D 0; i < pr->num_zones; i++) { + spz =3D &pr->spzones[i]; + + if (!scmi_powercap_is_control_type_child(spz)) + continue; + + ret =3D powercap_ops->cap_enable_get(spz->ph, spz->info->id, &enabled); + if (ret) + return ret; + + if (!enabled) { + *mode =3D false; + return 0; + } + } + + return 0; +} + +static int +scmi_powercap_set_root_children_enable_state(struct scmi_powercap_root *pr= , bool enable) +{ + struct scmi_powercap_zone *spz; + bool *prev_state; + int i, ret; + + prev_state =3D kcalloc(pr->num_zones, sizeof(*prev_state), GFP_KERNEL); + if (!prev_state) + return -ENOMEM; + + for (i =3D 0; i < pr->num_zones; i++) { + spz =3D &pr->spzones[i]; + + if (!scmi_powercap_is_control_type_child(spz)) + continue; + + if (!spz->info->powercap_cap_config) + continue; + + ret =3D powercap_ops->cap_enable_get(spz->ph, spz->info->id, + &prev_state[i]); + + if (ret) + goto revert; + + if (prev_state[i] =3D=3D enable) + continue; + + ret =3D powercap_ops->cap_enable_set(spz->ph, spz->info->id, enable); + if (ret) + goto revert; + } + + pr->enabled =3D enable; + kfree(prev_state); + return 0; + +revert: + while (--i >=3D 0) { + spz =3D &pr->spzones[i]; + + if (!scmi_powercap_is_control_type_child(spz)) + continue; + if (!spz->info->powercap_cap_config) + continue; + if (prev_state[i] =3D=3D enable) + continue; + + powercap_ops->cap_enable_set(spz->ph, spz->info->id, prev_state[i]); + } + + kfree(prev_state); + return ret; +} + +struct scmi_powercap_root_state { + struct scmi_powercap_root *pr; + bool prev_enabled; +}; + +static int +scmi_powercap_control_type_set_enable(struct powercap_control_type *pct, b= ool mode) +{ + struct scmi_powercap_root_state *states; + struct scmi_powercap_root *pr; + int num_roots =3D 0, changed =3D 0, ret =3D 0; + + mutex_lock(&scmi_powercap_roots_lock); + list_for_each_entry(pr, &scmi_powercap_roots, node) + num_roots++; + + states =3D kcalloc(num_roots, sizeof(*states), GFP_KERNEL); + if (!states) { + ret =3D -ENOMEM; + goto out_unlock; + } + + list_for_each_entry(pr, &scmi_powercap_roots, node) { + bool prev; + + ret =3D scmi_powercap_read_root_children_enable_state(pr, &prev); + if (ret) + goto revert; + + if (prev =3D=3D mode) + continue; + + ret =3D scmi_powercap_set_root_children_enable_state(pr, mode); + if (ret) + goto revert; + + states[changed].pr =3D pr; + states[changed].prev_enabled =3D prev; + changed++; + } + goto out_free; + +revert: + while (changed--) + scmi_powercap_set_root_children_enable_state(states[changed].pr, + states[changed].prev_enabled); + +out_free: + kfree(states); + +out_unlock: + mutex_unlock(&scmi_powercap_roots_lock); + + return ret; +} + +static int +scmi_powercap_control_type_get_enable(struct powercap_control_type *pct, b= ool *mode) +{ + struct scmi_powercap_root *pr; + int ret =3D 0; + + *mode =3D true; + + mutex_lock(&scmi_powercap_roots_lock); + list_for_each_entry(pr, &scmi_powercap_roots, node) { + ret =3D scmi_powercap_read_root_children_enable_state(pr, &pr->enabled); + + if (ret) + break; + if (!pr->enabled) { + *mode =3D false; + break; + } + } + mutex_unlock(&scmi_powercap_roots_lock); + + return ret; +} + +static const struct powercap_control_type_ops scmi_powercap_control_type_o= ps =3D { + .set_enable =3D scmi_powercap_control_type_set_enable, + .get_enable =3D scmi_powercap_control_type_get_enable, +}; + static int scmi_powercap_zone_release(struct powercap_zone *pz) { return 0; @@ -495,6 +675,16 @@ static int scmi_powercap_probe(struct scmi_device *sde= v) if (ret) return ret; =20 + INIT_LIST_HEAD(&pr->node); + + ret =3D scmi_powercap_read_root_children_enable_state(pr, &pr->enabled); + if (ret) + return ret; + + mutex_lock(&scmi_powercap_roots_lock); + list_add_tail(&pr->node, &scmi_powercap_roots); + mutex_unlock(&scmi_powercap_roots_lock); + dev_set_drvdata(dev, pr); =20 return ret; @@ -505,6 +695,10 @@ static void scmi_powercap_remove(struct scmi_device *s= dev) struct device *dev =3D &sdev->dev; struct scmi_powercap_root *pr =3D dev_get_drvdata(dev); =20 + mutex_lock(&scmi_powercap_roots_lock); + list_del(&pr->node); + mutex_unlock(&scmi_powercap_roots_lock); + scmi_powercap_unregister_all_zones(pr); } =20 @@ -525,7 +719,8 @@ static int __init scmi_powercap_init(void) { int ret; =20 - scmi_top_pcntrl =3D powercap_register_control_type(NULL, "arm-scmi", NULL= ); + scmi_top_pcntrl =3D powercap_register_control_type(NULL, "arm-scmi", + &scmi_powercap_control_type_ops); if (IS_ERR(scmi_top_pcntrl)) return PTR_ERR(scmi_top_pcntrl); =20 --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 21FC95AEC5D; Wed, 9 Sep 2026 22:04:53 +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=1788991496; cv=none; b=Zsi/PynppSXIura7TkuDjGUs/j1NsRleDEh7pLkc95ex1m2i5ilPjesfeMs4aD3e+1NVPSRJZUhmUtB0RGzQCRdHIfYJsROf1stwpwiIO6CDBzSqGFGaKrsYm8edXE449YzNVlqpheKgsC3rJli6Ih4JzwYMxEY4Bu1eEi+QhJI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991496; c=relaxed/simple; bh=VaIjpliNHMdmpjcZ2+EaV8d740iUXIoJDb8F//xzL00=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MKx3moAiNyjCpI8SC/ZN0Ej8kpxa0uysBC5qzsv6Sq0wUtfmNmIf24ckyrazxcfZxggpHF3KEC8dr6ik//NR2zdhEbUbP+xplz3J2utn8gX9D1CQfFh9RO32ONyDpvKWSiPd6FAqsF2R1wb+NTqzDX7TWDbJrpTSl/M7V2+QI14= 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=IMLUagqT; 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="IMLUagqT" 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 CC15D169C; Wed, 9 Sep 2026 15:04:49 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 3F1433F7D8; Wed, 9 Sep 2026 15:04:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991493; bh=VaIjpliNHMdmpjcZ2+EaV8d740iUXIoJDb8F//xzL00=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IMLUagqT5uhVayad3yYOTL4c4s8B3sW0Dz7KO0VGZ2bvvIo4b+G+c+IaqcDPNtq4g knQYeIjdQoeLdv/9OY0ahuOY1GqXZlUJ+mcz9NyB34YFsISBtvajXdBLrAJhYYLCsY bMbx1iYUaSI8L94NpY9hS/ie598YVqzUmkFSGfV8= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 02/13] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers Date: Wed, 9 Sep 2026 22:04:26 +0000 Message-Id: <20260909220437.1254412-3-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Starting from SCMIv4.0 the protocols DESCRIBE_FASTCHANNEL commands allow to specify one additional per-protocol custom field in the outgoing message request in order to, optionally, further narrow down the scope of the fastchannel discovery request; the related message-reply format is instead unchanged. Add an optional custom protocol parameter to the common fastchannel helper so as to enable the caller to choose the kind of message to send based on the detected protocol version. Signed-off-by: Cristian Marussi Signed-off-by: Philip Radford --- drivers/firmware/arm_scmi/driver.c | 12 ++++++++++-- drivers/firmware/arm_scmi/perf.c | 8 ++++---- drivers/firmware/arm_scmi/powercap.c | 8 ++++---- drivers/firmware/arm_scmi/protocols.h | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi= /driver.c index 3e0d975ec94c..81cb8eec14bc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1902,6 +1902,11 @@ static int scmi_iterator_run_bound(void *iter, unsig= ned int *start, struct scmi_msg_get_fc_info { __le32 domain; __le32 message_id; + __le32 custom; +#define MSG_FC_INFO_SZ_EXTENDED \ + (sizeof(struct scmi_msg_get_fc_info)) +#define MSG_FC_INFO_SZ \ + (sizeof(struct scmi_msg_get_fc_info) - sizeof(__le32)) }; =20 struct scmi_msg_resp_desc_fc { @@ -1930,7 +1935,7 @@ struct scmi_msg_resp_desc_fc { static void scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, u8 describe_id, u32 message_id, u32 valid_size, - u32 domain, void __iomem **p_addr, + u32 domain, u32 *custom, void __iomem **p_addr, struct scmi_fc_db_info **p_db, u32 *rate_limit) { int ret; @@ -1961,13 +1966,16 @@ scmi_common_fastchannel_init(const struct scmi_prot= ocol_handle *ph, } =20 ret =3D ph->xops->xfer_get_init(ph, describe_id, - sizeof(*info), sizeof(*resp), &t); + custom ? MSG_FC_INFO_SZ_EXTENDED : + MSG_FC_INFO_SZ, sizeof(*resp), &t); if (ret) goto err_out; =20 info =3D t->tx.buf; info->domain =3D cpu_to_le32(domain); info->message_id =3D cpu_to_le32(message_id); + if (custom) + info->custom =3D cpu_to_le32(*custom); =20 /* * Bail out on error leaving fc_info addresses zeroed; this includes diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/p= erf.c index 4583d02bee1c..7f283f457e02 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -835,25 +835,25 @@ static void scmi_perf_domain_init_fc(const struct scm= i_protocol_handle *ph, return; =20 ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, - PERF_LEVEL_GET, 4, dom->id, + PERF_LEVEL_GET, 4, dom->id, NULL, &fc[PERF_FC_LEVEL].get_addr, NULL, &fc[PERF_FC_LEVEL].rate_limit); =20 ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, - PERF_LIMITS_GET, 8, dom->id, + PERF_LIMITS_GET, 8, dom->id, NULL, &fc[PERF_FC_LIMIT].get_addr, NULL, &fc[PERF_FC_LIMIT].rate_limit); =20 if (dom->info.set_perf) ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, - PERF_LEVEL_SET, 4, dom->id, + PERF_LEVEL_SET, 4, dom->id, NULL, &fc[PERF_FC_LEVEL].set_addr, &fc[PERF_FC_LEVEL].set_db, &fc[PERF_FC_LEVEL].rate_limit); =20 if (dom->set_limits) ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, - PERF_LIMITS_SET, 8, dom->id, + PERF_LIMITS_SET, 8, dom->id, NULL, &fc[PERF_FC_LIMIT].set_addr, &fc[PERF_FC_LIMIT].set_db, &fc[PERF_FC_LIMIT].rate_limit); diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 27e3c805e927..31b19967452f 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -726,24 +726,24 @@ static void scmi_powercap_domain_init_fc(const struct= scmi_protocol_handle *ph, return; =20 ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_CAP_SET, 4, domain, + POWERCAP_CAP_SET, 4, domain, NULL, &fc[POWERCAP_FC_CAP].set_addr, &fc[POWERCAP_FC_CAP].set_db, &fc[POWERCAP_FC_CAP].rate_limit); =20 ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_CAP_GET, 4, domain, + POWERCAP_CAP_GET, 4, domain, NULL, &fc[POWERCAP_FC_CAP].get_addr, NULL, &fc[POWERCAP_FC_CAP].rate_limit); =20 ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_PAI_SET, 4, domain, + POWERCAP_PAI_SET, 4, domain, NULL, &fc[POWERCAP_FC_PAI].set_addr, &fc[POWERCAP_FC_PAI].set_db, &fc[POWERCAP_FC_PAI].rate_limit); =20 ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_PAI_GET, 4, domain, + POWERCAP_PAI_GET, 4, domain, NULL, &fc[POWERCAP_FC_PAI].get_addr, NULL, &fc[POWERCAP_FC_PAI].rate_limit); =20 diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_s= cmi/protocols.h index 15ad5162e37a..6b27e1b531a1 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -291,7 +291,7 @@ struct scmi_proto_helpers_ops { u32 message_id, u32 *attributes); void (*fastchannel_init)(const struct scmi_protocol_handle *ph, u8 describe_id, u32 message_id, - u32 valid_size, u32 domain, + u32 valid_size, u32 domain, u32 *custom, void __iomem **p_addr, struct scmi_fc_db_info **p_db, u32 *rate_limit); --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3823A45D900; Wed, 9 Sep 2026 22:04:56 +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=1788991501; cv=none; b=CPVpG7GktHl6Y6OTV/KOo51gi7yrUDD55OvjCV2Aq2AKbCBPShSTG7rcbQyr46gDqPZSA0453Ps9+wahvU5uZTYz3JIPc5mIeitBnQuDxMDVZLRUK1yA9okm2Ss9bRvdNMQzLsPEQx8HLWecmEDSjPTFhe1yBpJcAiV2c20oBDU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991501; c=relaxed/simple; bh=Tv1wpslTe6McWKsVqGu4NNRTcA5M9UN8sGTJ4eUQ9Nc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mvii3W3n6oM/HfmJBiDQQTtT1New9tHVPjxF0rCVm5IqNwaiCQoLPfYSMRaBHm17Wv+gcYuHMuOAXL/xwU9n/MYd98c2+XacKR67XxIXaDwgFJpC5WOmzEcorB9cc3kGzRMsKDsuoJxGvMj22pfqNMA8JHZjaD8XQIeN8cplh1s= 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=aitxkOzK; 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="aitxkOzK" 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 6F49B1D31; Wed, 9 Sep 2026 15:04:52 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A18543F7D8; Wed, 9 Sep 2026 15:04:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991496; bh=Tv1wpslTe6McWKsVqGu4NNRTcA5M9UN8sGTJ4eUQ9Nc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aitxkOzKIHgvTjFXbUqkxsD2yFhjlWCd1zlb2S4GAKuVwD+LgjdtyiMUph1a7Llpr C2xkXsnBMRbrSvuIRgTKBMtDUf9a4fxO9uH1bmsIDel5aeXLFikYxkl3UAMga5KCyx 0PhjoBnM2R9QgQ3HM2PwIDDzO6t/KUaQDVLSI3e8= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 03/13] firmware: arm_scmi: Refactor powercap domain layout Date: Wed, 9 Sep 2026 22:04:27 +0000 Message-Id: <20260909220437.1254412-4-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi SCMIv4.0 introduces the idea of an optional Concurrent Power Limit (CPL) for each powercap domain, where CPL0 coincides with the one and only per-domain constraint limit that was available in pre-v4.0 SCMI Powercap. Refactor the powercap domain descriptors and powercap operations to allow future v4.0 extensions to cope with multiple CPLs. While at that generalize the powercap protocol API to drop PAI references in favour of a more generic avg_ivl naming, since from v4.0 the number and types of averaging intervals will change in a non-backward compatible way, so let's bury these changes within the protocol layer. Last but not least, make the necessary changes to the ARM SCMI Powwercap driver in order to support all of these new capabilities. No functional change. Signed-off-by: Cristian Marussi [Philip: Adjusted domain_id comparitor in scmi_powercap_pai_get] Signed-off-by: Philip Radford --- V7->V8 - Fixed conflict due to changes in 7.2-rc1 --- drivers/firmware/arm_scmi/powercap.c | 182 +++++++++++++++++---------- drivers/powercap/arm_scmi_powercap.c | 52 ++++---- include/linux/scmi_protocol.h | 74 +++++++---- 3 files changed, 189 insertions(+), 119 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 31b19967452f..4666030176c3 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) Powercap Protocol * - * Copyright (C) 2022 ARM Ltd. + * Copyright (C) 2022-2026 ARM Ltd. */ =20 #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt @@ -20,6 +20,8 @@ /* Updated only after ALL the mandatory features for that version are merg= ed */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000 =20 +#define CPL0 0 + enum scmi_powercap_protocol_cmd { POWERCAP_DOMAIN_ATTRIBUTES =3D 0x3, POWERCAP_CAP_GET =3D 0x4, @@ -192,27 +194,26 @@ scmi_powercap_validate(unsigned int min_val, unsigned= int max_val, =20 static int scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, - struct powercap_info *pinfo, u32 domain) + struct powercap_info *pinfo, + struct scmi_powercap_info *dom_info) { int ret; u32 flags; struct scmi_xfer *t; - struct scmi_powercap_info *dom_info =3D pinfo->powercaps + domain; struct scmi_msg_resp_powercap_domain_attributes *resp; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, - sizeof(domain), sizeof(*resp), &t); + sizeof(dom_info->id), sizeof(*resp), &t); if (ret) return ret; =20 - put_unaligned_le32(domain, t->tx.buf); + put_unaligned_le32(dom_info->id, t->tx.buf); resp =3D t->rx.buf; =20 ret =3D ph->xops->do_xfer(ph, t); if (!ret) { flags =3D le32_to_cpu(resp->attributes); =20 - dom_info->id =3D domain; if (pinfo->notify_cap_cmd) dom_info->notify_powercap_cap_change =3D SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); @@ -221,12 +222,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_= protocol_handle *ph, SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); dom_info->async_powercap_cap_set =3D SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); - dom_info->powercap_cap_config =3D - SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); + dom_info->powercap_monitoring =3D SUPPORTS_POWERCAP_MONITORING(flags); - dom_info->powercap_pai_config =3D - SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); dom_info->powercap_scale_mw =3D SUPPORTS_POWER_UNITS_MW(flags); dom_info->powercap_scale_uw =3D @@ -236,13 +234,29 @@ scmi_powercap_domain_attributes_get(const struct scmi= _protocol_handle *ph, =20 strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); =20 - dom_info->min_pai =3D le32_to_cpu(resp->min_pai); - dom_info->max_pai =3D le32_to_cpu(resp->max_pai); - dom_info->pai_step =3D le32_to_cpu(resp->pai_step); - ret =3D scmi_powercap_validate(dom_info->min_pai, - dom_info->max_pai, - dom_info->pai_step, - dom_info->powercap_pai_config); + dom_info->sustainable_power =3D + le32_to_cpu(resp->sustainable_power); + dom_info->accuracy =3D le32_to_cpu(resp->accuracy); + + dom_info->parent_id =3D le32_to_cpu(resp->parent_id); + if (dom_info->parent_id !=3D SCMI_POWERCAP_ROOT_ZONE_ID && + (dom_info->parent_id >=3D pinfo->num_domains || + dom_info->parent_id =3D=3D dom_info->id)) { + dev_err(ph->dev, + "Platform reported inconsistent parent ID for domain %d - %s\n", + dom_info->id, dom_info->name); + ret =3D -ENODEV; + } + + dom_info->cpli[0].avg_ivl_config =3D + SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); + dom_info->cpli[0].min_avg_ivl =3D le32_to_cpu(resp->min_pai); + dom_info->cpli[0].max_avg_ivl =3D le32_to_cpu(resp->max_pai); + dom_info->cpli[0].avg_ivl_step =3D le32_to_cpu(resp->pai_step); + ret =3D scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl, + dom_info->cpli[0].max_avg_ivl, + dom_info->cpli[0].avg_ivl_step, + dom_info->cpli[0].avg_ivl_config); if (ret) { dev_err(ph->dev, "Platform reported inconsistent PAI config for domain %d - %s\n", @@ -250,13 +264,15 @@ scmi_powercap_domain_attributes_get(const struct scmi= _protocol_handle *ph, goto clean; } =20 - dom_info->min_power_cap =3D le32_to_cpu(resp->min_power_cap); - dom_info->max_power_cap =3D le32_to_cpu(resp->max_power_cap); - dom_info->power_cap_step =3D le32_to_cpu(resp->power_cap_step); - ret =3D scmi_powercap_validate(dom_info->min_power_cap, - dom_info->max_power_cap, - dom_info->power_cap_step, - dom_info->powercap_cap_config); + dom_info->cpli[0].cap_config =3D + SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); + dom_info->cpli[0].min_power_cap =3D le32_to_cpu(resp->min_power_cap); + dom_info->cpli[0].max_power_cap =3D le32_to_cpu(resp->max_power_cap); + dom_info->cpli[0].power_cap_step =3D le32_to_cpu(resp->power_cap_step); + ret =3D scmi_powercap_validate(dom_info->cpli[0].min_power_cap, + dom_info->cpli[0].max_power_cap, + dom_info->cpli[0].power_cap_step, + dom_info->cpli[0].cap_config); if (ret) { dev_err(ph->dev, "Platform reported inconsistent CAP config for domain %d - %s\n", @@ -264,19 +280,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_= protocol_handle *ph, goto clean; } =20 - dom_info->sustainable_power =3D - le32_to_cpu(resp->sustainable_power); - dom_info->accuracy =3D le32_to_cpu(resp->accuracy); - - dom_info->parent_id =3D le32_to_cpu(resp->parent_id); - if (dom_info->parent_id !=3D SCMI_POWERCAP_ROOT_ZONE_ID && - (dom_info->parent_id >=3D pinfo->num_domains || - dom_info->parent_id =3D=3D dom_info->id)) { - dev_err(ph->dev, - "Platform reported inconsistent parent ID for domain %d - %s\n", - dom_info->id, dom_info->name); - ret =3D -ENODEV; - } + /* Just using same short name */ + strscpy(dom_info->cpli[0].name, dom_info->name, + SCMI_SHORT_NAME_MAX_SIZE); } =20 clean: @@ -288,12 +294,30 @@ scmi_powercap_domain_attributes_get(const struct scmi= _protocol_handle *ph, */ if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, - domain, NULL, dom_info->name, + dom_info->id, NULL, dom_info->name, SCMI_MAX_STR_SIZE); =20 return ret; } =20 +static int +scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph, + struct powercap_info *pinfo, u32 domain) +{ + struct scmi_powercap_info *dom_info =3D pinfo->powercaps + domain; + + dom_info->num_cpli =3D 1; + dom_info->cpli =3D devm_kcalloc(ph->dev, dom_info->num_cpli, + sizeof(*dom_info->cpli), GFP_KERNEL); + if (!dom_info->cpli) + return -ENOMEM; + + dom_info->id =3D domain; + dom_info->cpli[0].id =3D CPL0; + + return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info); +} + static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle= *ph) { struct powercap_info *pi =3D ph->get_priv(ph); @@ -335,10 +359,11 @@ static int scmi_powercap_xfer_cap_get(const struct sc= mi_protocol_handle *ph, =20 static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, const struct scmi_powercap_info *dom, - u32 *power_cap) + u32 cpl_id, u32 *power_cap) { - if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) { - *power_cap =3D ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr); + if (dom->cpli[cpl_id].fc_info && + dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) { + *power_cap =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_a= ddr); trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET, dom->id, *power_cap, 0); return 0; @@ -348,7 +373,7 @@ static int __scmi_powercap_cap_get(const struct scmi_pr= otocol_handle *ph, } =20 static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 *power_cap) + u32 domain_id, u32 cpl_id, u32 *power_cap) { const struct scmi_powercap_info *dom; =20 @@ -359,12 +384,13 @@ static int scmi_powercap_cap_get(const struct scmi_pr= otocol_handle *ph, if (!dom) return -EINVAL; =20 - return __scmi_powercap_cap_get(ph, dom, power_cap); + return __scmi_powercap_cap_get(ph, dom, cpl_id, power_cap); } =20 static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *p= h, const struct scmi_powercap_info *pc, - u32 power_cap, bool ignore_dresp) + u32 cpl_id, u32 power_cap, + bool ignore_dresp) { int ret; struct scmi_xfer *t; @@ -406,21 +432,23 @@ static int scmi_powercap_xfer_cap_set(const struct sc= mi_protocol_handle *ph, =20 static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, struct powercap_info *pi, u32 domain_id, - u32 power_cap, bool ignore_dresp) + u32 cpl_id, u32 power_cap, bool ignore_dresp) { int ret =3D -EINVAL; const struct scmi_powercap_info *pc; =20 pc =3D scmi_powercap_dom_info_get(ph, domain_id); - if (!pc || !pc->powercap_cap_config) + if (!pc || !pc->cpli[cpl_id].cap_config) return ret; =20 if (power_cap && - (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap)) + (power_cap < pc->cpli[cpl_id].min_power_cap || + power_cap > pc->cpli[cpl_id].max_power_cap)) return ret; =20 - if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) { - struct scmi_fc_info *fci =3D &pc->fc_info[POWERCAP_FC_CAP]; + if (pc->cpli[cpl_id].fc_info && + pc->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].set_addr) { + struct scmi_fc_info *fci =3D &pc->cpli[cpl_id].fc_info[POWERCAP_FC_CAP]; =20 iowrite32(power_cap, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); @@ -428,7 +456,7 @@ static int __scmi_powercap_cap_set(const struct scmi_pr= otocol_handle *ph, domain_id, power_cap, 0); ret =3D 0; } else { - ret =3D scmi_powercap_xfer_cap_set(ph, pc, power_cap, + ret =3D scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp); } =20 @@ -440,7 +468,7 @@ static int __scmi_powercap_cap_set(const struct scmi_pr= otocol_handle *ph, } =20 static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 power_cap, + u32 domain_id, u32 cpl_id, u32 power_cap, bool ignore_dresp) { struct powercap_info *pi =3D ph->get_priv(ph); @@ -463,7 +491,7 @@ static int scmi_powercap_cap_set(const struct scmi_prot= ocol_handle *ph, } } =20 - return __scmi_powercap_cap_set(ph, pi, domain_id, + return __scmi_powercap_cap_set(ph, pi, domain_id, cpl_id, power_cap, ignore_dresp); } =20 @@ -489,7 +517,7 @@ static int scmi_powercap_xfer_pai_get(const struct scmi= _protocol_handle *ph, } =20 static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 *pai) + u32 domain_id, u32 cpl_id, u32 *pai) { struct scmi_powercap_info *dom; struct powercap_info *pi =3D ph->get_priv(ph); @@ -498,8 +526,11 @@ static int scmi_powercap_pai_get(const struct scmi_pro= tocol_handle *ph, return -EINVAL; =20 dom =3D pi->powercaps + domain_id; - if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) { - *pai =3D ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr); + if (cpl_id >=3D dom->num_cpli) + return -EINVAL; + + if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PA= I].get_addr) { + *pai =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr); trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET, domain_id, *pai, 0); return 0; @@ -508,6 +539,12 @@ static int scmi_powercap_pai_get(const struct scmi_pro= tocol_handle *ph, return scmi_powercap_xfer_pai_get(ph, domain_id, pai); } =20 +static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handl= e *ph, + u32 domain_id, u32 cpl_id, u32 *val) +{ + return scmi_powercap_pai_get(ph, domain_id, cpl_id, val); +} + static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *p= h, u32 domain_id, u32 pai) { @@ -532,17 +569,18 @@ static int scmi_powercap_xfer_pai_set(const struct sc= mi_protocol_handle *ph, } =20 static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 pai) + u32 domain_id, u32 cpl_id, u32 pai) { const struct scmi_powercap_info *pc; =20 pc =3D scmi_powercap_dom_info_get(ph, domain_id); - if (!pc || !pc->powercap_pai_config || !pai || - pai < pc->min_pai || pai > pc->max_pai) + if (!pc || cpl_id >=3D pc->num_cpli || !pc->cpli[cpl_id].avg_ivl_config || + !pai || pai < pc->cpli[cpl_id].min_avg_ivl || + pai > pc->cpli[cpl_id].max_avg_ivl) return -EINVAL; =20 - if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) { - struct scmi_fc_info *fci =3D &pc->fc_info[POWERCAP_FC_PAI]; + if (pc->cpli[cpl_id].fc_info && pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI]= .set_addr) { + struct scmi_fc_info *fci =3D &pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI]; =20 trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET, domain_id, pai, 0); @@ -554,6 +592,12 @@ static int scmi_powercap_pai_set(const struct scmi_pro= tocol_handle *ph, return scmi_powercap_xfer_pai_set(ph, domain_id, pai); } =20 +static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handl= e *ph, + u32 domain_id, u32 cpl_id, u32 val) +{ + return scmi_powercap_pai_set(ph, domain_id, cpl_id, val); +} + static int scmi_powercap_measurements_get(const struct scmi_protocol_handl= e *ph, u32 domain_id, u32 *average_power, u32 *pai) @@ -652,11 +696,11 @@ static int scmi_powercap_cap_enable_set(const struct = scmi_protocol_handle *ph, if (!pi->states[domain_id].last_pcap) return -EINVAL; =20 - ret =3D __scmi_powercap_cap_set(ph, pi, domain_id, + ret =3D __scmi_powercap_cap_set(ph, pi, domain_id, CPL0, pi->states[domain_id].last_pcap, true); } else { - ret =3D __scmi_powercap_cap_set(ph, pi, domain_id, 0, true); + ret =3D __scmi_powercap_cap_set(ph, pi, domain_id, CPL0, 0, true); } =20 if (ret) @@ -667,7 +711,7 @@ static int scmi_powercap_cap_enable_set(const struct sc= mi_protocol_handle *ph, * server could have ignored a disable request and kept enforcing some * powercap limit requested by other agents. */ - ret =3D scmi_powercap_cap_get(ph, domain_id, &power_cap); + ret =3D scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap); if (!ret) pi->states[domain_id].enabled =3D !!power_cap; =20 @@ -692,7 +736,7 @@ static int scmi_powercap_cap_enable_get(const struct sc= mi_protocol_handle *ph, * Report always real platform state; platform could have ignored * a previous disable request. Default true on any error. */ - ret =3D scmi_powercap_cap_get(ph, domain_id, &power_cap); + ret =3D scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap); if (!ret) *enable =3D !!power_cap; =20 @@ -709,8 +753,8 @@ static const struct scmi_powercap_proto_ops powercap_pr= oto_ops =3D { .cap_set =3D scmi_powercap_cap_set, .cap_enable_set =3D scmi_powercap_cap_enable_set, .cap_enable_get =3D scmi_powercap_cap_enable_get, - .pai_get =3D scmi_powercap_pai_get, - .pai_set =3D scmi_powercap_pai_set, + .avg_interval_get =3D scmi_powercap_avg_interval_get, + .avg_interval_set =3D scmi_powercap_avg_interval_set, .measurements_get =3D scmi_powercap_measurements_get, .measurements_threshold_set =3D scmi_powercap_measurements_threshold_set, .measurements_threshold_get =3D scmi_powercap_measurements_threshold_get, @@ -1001,18 +1045,18 @@ scmi_powercap_protocol_init(const struct scmi_proto= col_handle *ph) * formed and correlated by sane parent-child relationship (if any). */ for (domain =3D 0; domain < pinfo->num_domains; domain++) { - ret =3D scmi_powercap_domain_attributes_get(ph, pinfo, domain); + ret =3D scmi_powercap_domain_initialize(ph, pinfo, domain); if (ret) return ret; =20 if (pinfo->powercaps[domain].fastchannels) scmi_powercap_domain_init_fc(ph, domain, - &pinfo->powercaps[domain].fc_info); + &pinfo->powercaps[domain].cpli[CPL0].fc_info); =20 /* Grab initial state when disable is supported. */ if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2) { ret =3D __scmi_powercap_cap_get(ph, - &pinfo->powercaps[domain], + &pinfo->powercaps[domain], CPL0, &pinfo->states[domain].last_pcap); if (ret) return ret; diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index 96d55921fefc..7c2dbba5002c 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -123,7 +123,7 @@ scmi_powercap_set_root_children_enable_state(struct scm= i_powercap_root *pr, bool =20 if (!scmi_powercap_is_control_type_child(spz)) continue; - if (!spz->info->powercap_cap_config) + if (!spz->info->cpli[0].cap_config) continue; if (prev_state[i] =3D=3D enable) continue; @@ -277,7 +277,7 @@ static const struct powercap_zone_ops zone_ops =3D { }; =20 static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *s= pz, - u64 power_limit_uw, u32 *norm) + u64 power_limit_uw, int cid, u32 *norm) { bool scale_mw =3D spz->info->powercap_scale_mw; u64 val; @@ -288,9 +288,9 @@ static void scmi_powercap_normalize_cap(const struct sc= mi_powercap_zone *spz, * the range [min_power_cap, max_power_cap] whose bounds are assured to * be two unsigned 32bits quantities. */ - *norm =3D clamp_t(u32, val, spz->info->min_power_cap, - spz->info->max_power_cap); - *norm =3D rounddown(*norm, spz->info->power_cap_step); + *norm =3D clamp_t(u32, val, spz->info->cpli[cid].min_power_cap, + spz->info->cpli[cid].max_power_cap); + *norm =3D rounddown(*norm, spz->info->cpli[cid].power_cap_step); =20 val =3D (scale_mw) ? *norm * 1000 : *norm; if (power_limit_uw !=3D val) @@ -305,12 +305,12 @@ static int scmi_powercap_set_power_limit_uw(struct po= wercap_zone *pz, int cid, struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); u32 norm_power; =20 - if (!spz->info->powercap_cap_config) + if (!spz->info->cpli[cid].cap_config) return -EINVAL; =20 - scmi_powercap_normalize_cap(spz, power_uw, &norm_power); + scmi_powercap_normalize_cap(spz, power_uw, cid, &norm_power); =20 - return powercap_ops->cap_set(spz->ph, spz->info->id, norm_power, false); + return powercap_ops->cap_set(spz->ph, spz->info->id, cid, norm_power, fal= se); } =20 static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int = cid, @@ -320,7 +320,7 @@ static int scmi_powercap_get_power_limit_uw(struct powe= rcap_zone *pz, int cid, u32 power; int ret; =20 - ret =3D powercap_ops->cap_get(spz->ph, spz->info->id, &power); + ret =3D powercap_ops->cap_get(spz->ph, spz->info->id, cid, &power); if (ret) return ret; =20 @@ -332,19 +332,20 @@ static int scmi_powercap_get_power_limit_uw(struct po= wercap_zone *pz, int cid, } =20 static void scmi_powercap_normalize_time(const struct scmi_powercap_zone *= spz, - u64 time_us, u32 *norm) + u64 time_us, int cid, u32 *norm) { /* * This cast is lossless since here @time_us is certain to be within the - * range [min_pai, max_pai] whose bounds are assured to be two unsigned - * 32bits quantities. + * range [min_avg_ivl, max_avg_ivl] whose bounds are assured to be two + * unsigned 32bits quantities. */ - *norm =3D clamp_t(u32, time_us, spz->info->min_pai, spz->info->max_pai); - *norm =3D rounddown(*norm, spz->info->pai_step); + *norm =3D clamp_t(u32, time_us, spz->info->cpli[cid].min_avg_ivl, + spz->info->cpli[cid].max_avg_ivl); + *norm =3D rounddown(*norm, spz->info->cpli[cid].avg_ivl_step); =20 if (time_us !=3D *norm) dev_dbg(spz->dev, - "Normalized %s:PAI - requested:%llu - normalized:%u\n", + "Normalized %s:AVG_IVL - requested:%llu - normalized:%u\n", spz->info->name, time_us, *norm); } =20 @@ -354,12 +355,13 @@ static int scmi_powercap_set_time_window_us(struct po= wercap_zone *pz, int cid, struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); u32 norm_pai; =20 - if (!spz->info->powercap_pai_config) + if (!spz->info->cpli[cid].avg_ivl_config) return -EINVAL; =20 - scmi_powercap_normalize_time(spz, time_window_us, &norm_pai); + scmi_powercap_normalize_time(spz, time_window_us, cid, &norm_pai); =20 - return powercap_ops->pai_set(spz->ph, spz->info->id, norm_pai); + return powercap_ops->avg_interval_set(spz->ph, spz->info->id, + cid, norm_pai); } =20 static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int = cid, @@ -369,7 +371,7 @@ static int scmi_powercap_get_time_window_us(struct powe= rcap_zone *pz, int cid, int ret; u32 pai; =20 - ret =3D powercap_ops->pai_get(spz->ph, spz->info->id, &pai); + ret =3D powercap_ops->avg_interval_get(spz->ph, spz->info->id, cid, &pai); if (ret) return ret; =20 @@ -383,7 +385,7 @@ static int scmi_powercap_get_max_power_uw(struct powerc= ap_zone *pz, int cid, { struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); =20 - *max_power_uw =3D spz->info->max_power_cap; + *max_power_uw =3D spz->info->cpli[cid].max_power_cap; if (spz->info->powercap_scale_mw) *max_power_uw *=3D 1000; =20 @@ -395,7 +397,7 @@ static int scmi_powercap_get_min_power_uw(struct powerc= ap_zone *pz, int cid, { struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); =20 - *min_power_uw =3D spz->info->min_power_cap; + *min_power_uw =3D spz->info->cpli[cid].min_power_cap; if (spz->info->powercap_scale_mw) *min_power_uw *=3D 1000; =20 @@ -407,7 +409,7 @@ static int scmi_powercap_get_max_time_window_us(struct = powercap_zone *pz, { struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); =20 - *time_window_us =3D spz->info->max_pai; + *time_window_us =3D spz->info->cpli[cid].max_avg_ivl; =20 return 0; } @@ -417,14 +419,16 @@ static int scmi_powercap_get_min_time_window_us(struc= t powercap_zone *pz, { struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); =20 - *time_window_us =3D (u64)spz->info->min_pai; + *time_window_us =3D (u64)spz->info->cpli[cid].min_avg_ivl; =20 return 0; } =20 static const char *scmi_powercap_get_name(struct powercap_zone *pz, int ci= d) { - return "SCMI power-cap"; + struct scmi_powercap_zone *spz =3D to_scmi_powercap_zone(pz); + + return spz->info->cpli[cid].name; } =20 static const struct powercap_zone_constraint_ops constraint_ops =3D { diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 5ab73b1ab9aa..ecd99b24ae92 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -2,7 +2,7 @@ /* * SCMI Message Protocol driver header * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2026 ARM Ltd. */ =20 #ifndef _LINUX_SCMI_PROTOCOL_H @@ -613,6 +613,35 @@ struct scmi_voltage_proto_ops { s32 *volt_uV); }; =20 +/** + * struct scmi_powercap_cpl_info - Describe one CPL - Concurrent Powercap= Limit + * + * @id: CPL ID as advertised by the platform. + * @cap_config: CAP configuration support for this CPL. + * @min_power_cap: Minimum configurable CAP. + * @max_power_cap: Maximum configurable CAP. + * @power_cap_step: Step size between two consecutive CAP values. + * @avg_ivl_config: Powercap averaging interval configuration support. + * @min_avg_ivl: Minimum configurable powercap averaging interval. + * @max_avg_ivl: Maximum configurable powercap averaging interval. + * @avg_ivl_step: Step size between two consecutive averaging intervals. + * @name: name assigned to the Powercap Domain by platform. + * @fc_info: Reference to the FastChannels descriptors supported by this C= PL + */ +struct scmi_powercap_cpl_info { + unsigned int id; + bool cap_config; + unsigned int min_power_cap; + unsigned int max_power_cap; + unsigned int power_cap_step; + bool avg_ivl_config; + unsigned int min_avg_ivl; + unsigned int max_avg_ivl; + unsigned int avg_ivl_step; + char name[SCMI_SHORT_NAME_MAX_SIZE]; + struct scmi_fc_info *fc_info; +}; + /** * struct scmi_powercap_info - Describe one available Powercap domain * @@ -621,21 +650,15 @@ struct scmi_voltage_proto_ops { * @notify_powercap_measurement_change: MEASUREMENTS change notifications * support. * @async_powercap_cap_set: Asynchronous CAP set support. - * @powercap_cap_config: CAP configuration support. * @powercap_monitoring: Monitoring (measurements) support. - * @powercap_pai_config: PAI configuration support. * @powercap_scale_mw: Domain reports power data in milliwatt units. * @powercap_scale_uw: Domain reports power data in microwatt units. * Note that, when both @powercap_scale_mw and * @powercap_scale_uw are set to false, the domain * reports power data on an abstract linear scale. + * @extended_names: Support for long names. + * @fastchannels: Support for at least one fastchannel, * @name: name assigned to the Powercap Domain by platform. - * @min_pai: Minimum configurable PAI. - * @max_pai: Maximum configurable PAI. - * @pai_step: Step size between two consecutive PAI values. - * @min_power_cap: Minimum configurable CAP. - * @max_power_cap: Maximum configurable CAP. - * @power_cap_step: Step size between two consecutive CAP values. * @sustainable_power: Maximum sustainable power consumption for this doma= in * under normal conditions. * @accuracy: The accuracy with which the power is measured and reported in @@ -643,30 +666,25 @@ struct scmi_voltage_proto_ops { * @parent_id: Identifier of the containing parent power capping domain, o= r the * value 0xFFFFFFFF if this powercap domain is a root domain not * contained in any other domain. + * @num_cpli: Number of discovered CPLs. + * @cpli: Reference to an array holding descriptors to all the discovered = CPLs. */ struct scmi_powercap_info { unsigned int id; bool notify_powercap_cap_change; bool notify_powercap_measurement_change; bool async_powercap_cap_set; - bool powercap_cap_config; bool powercap_monitoring; - bool powercap_pai_config; bool powercap_scale_mw; bool powercap_scale_uw; bool fastchannels; char name[SCMI_MAX_STR_SIZE]; - unsigned int min_pai; - unsigned int max_pai; - unsigned int pai_step; - unsigned int min_power_cap; - unsigned int max_power_cap; - unsigned int power_cap_step; unsigned int sustainable_power; unsigned int accuracy; #define SCMI_POWERCAP_ROOT_ZONE_ID 0xFFFFFFFFUL unsigned int parent_id; - struct scmi_fc_info *fc_info; + unsigned int num_cpli; + struct scmi_powercap_cpl_info *cpli; }; =20 /** @@ -695,8 +713,12 @@ struct scmi_powercap_info { * on the system: for this reason @cap_get and @cap_enable_get * will always report the final platform view of the powercaps. * @cap_enable_get: get the current CAP enable status for the specified do= main. - * @pai_get: get the current PAI value for the specified domain. - * @pai_set: set the PAI value for the specified domain to the provided va= lue. + * @avg_interval_get: get the current averaging interval value for the spe= cified + * domain. This will get the PAI or CAI depending on the used + * protocol version. + * @avg_interval_set: set the current averaging interval value for the spe= cified + * domain. This will set the PAI or CAI depending on the used + * protocol version. * @measurements_get: retrieve the current average power measurements for = the * specified domain and the related PAI upon which is * calculated. @@ -720,17 +742,17 @@ struct scmi_powercap_proto_ops { const struct scmi_powercap_info __must_check *(*info_get) (const struct scmi_protocol_handle *ph, u32 domain_id); int (*cap_get)(const struct scmi_protocol_handle *ph, u32 domain_id, - u32 *power_cap); + u32 cpl_id, u32 *power_cap); int (*cap_set)(const struct scmi_protocol_handle *ph, u32 domain_id, - u32 power_cap, bool ignore_dresp); + u32 cpl_id, u32 power_cap, bool ignore_dresp); int (*cap_enable_set)(const struct scmi_protocol_handle *ph, u32 domain_id, bool enable); int (*cap_enable_get)(const struct scmi_protocol_handle *ph, u32 domain_id, bool *enable); - int (*pai_get)(const struct scmi_protocol_handle *ph, u32 domain_id, - u32 *pai); - int (*pai_set)(const struct scmi_protocol_handle *ph, u32 domain_id, - u32 pai); + int (*avg_interval_get)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 *val); + int (*avg_interval_set)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 val); int (*measurements_get)(const struct scmi_protocol_handle *ph, u32 domain_id, u32 *average_power, u32 *pai); int (*measurements_threshold_set)(const struct scmi_protocol_handle *ph, --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 241F83F210A; Wed, 9 Sep 2026 22:04:59 +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=1788991501; cv=none; b=CCthUIJ2i8lllNQeJuVX4jA6xOijyA1y5XcFUfUCBuvHC7bLX+KJLEH6cn+RIeAe4CPJAbppt5fhIU/rzfb0EayyoFrmLB9Fo899/uDKCw+M1Etm7zBeC6TKscRAntgwjuAAAzdEfimhHHgIOAelVkXMUrWNBxJrGqAjZcIaVhc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991501; c=relaxed/simple; bh=zbL3g4ZseXM/rxQi03bOBSesLNwnYVQ1HasS/Y0NHoQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=DHCYtzauVsuirHfFoRyw+5tebuNnzynliVwLv2TSCAKygcGdZ5gTN8PVHMbOYbgrHzNlnCLh3JrdE+VPwMVr8G/DVDJEm0nn1mhNJC566Y9cZG+eotM38hcApT+s/OA0ypHgdoOpEJBs8OO6uZF59dXn49nhDwi1uM49Jh/Dfrs= 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=HIs0BEhC; 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="HIs0BEhC" 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 ED736169C; Wed, 9 Sep 2026 15:04:54 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4689E3FA32; Wed, 9 Sep 2026 15:04:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991498; bh=zbL3g4ZseXM/rxQi03bOBSesLNwnYVQ1HasS/Y0NHoQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HIs0BEhCWdMr979fsoQ2qqadHEpHIoyVIdYUt3gNdWWQEW4LNV7v9BD00OECtQaYv vtsILFlYJeYF2bKMgmyuFTa/VtvIDnC0/wpSwTadNCPyry0kx/uXFoPAng4B2IEUj7 OfpuUVyzA17gjyPWK/TGLOxZsbdaToLKkRodGMyY= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 04/13] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support Date: Wed, 9 Sep 2026 22:04:28 +0000 Message-Id: <20260909220437.1254412-5-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Add SCMIv4.0 Powercap support for enumerating multiple CPLs of a domain when available. Signed-off-by: Cristian Marussi [Philip: Fixed sparse issues where int was expected] Signed-off-by: Philip Radford --- V8->V9 - included validation of the number of cpls in iter_powercap_cpls_update_st= ate - re-ordered validation of cap_config and num_cpli in powercap_domain_attrs= _process - included validation of length in powercap_domain_attributes_get - amended cap_get and cap_set referencing apl_id array index instead fo fir= mware CPL identifier - added bounds check to make sure cpl_id is less than num_cpli --- drivers/firmware/arm_scmi/powercap.c | 495 ++++++++++++++++++++++----- drivers/powercap/arm_scmi_powercap.c | 2 +- include/linux/scmi_protocol.h | 1 + 3 files changed, 402 insertions(+), 96 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 4666030176c3..7ee39434a4db 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -33,6 +33,7 @@ enum scmi_powercap_protocol_cmd { POWERCAP_CAP_NOTIFY =3D 0xa, POWERCAP_MEASUREMENTS_NOTIFY =3D 0xb, POWERCAP_DESCRIBE_FASTCHANNEL =3D 0xc, + POWERCAP_CPC_ATTRIBUTES =3D 0xd, }; =20 enum { @@ -69,17 +70,56 @@ struct scmi_msg_resp_powercap_domain_attributes { __le32 parent_id; }; =20 +struct scmi_msg_resp_powercap_domain_attributes_v3 { + __le32 attributes; +#define SUPPORTS_POWERCAP_MAI_CONFIGURATION(x) ((x) & BIT(25)) +#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22)) +#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(x) ((x) & BIT(21)) +#define SUPPORTS_POWERCAP_CAI_CONFIGURATION(x) ((x) & BIT(20)) + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; + __le32 min_mai; + __le32 max_mai; + __le32 mai_step; + __le32 min_power_cap; + __le32 max_power_cap; + __le32 power_cap_step; + __le32 sustainable_power; + __le32 accuracy; + __le32 parent_id; + __le32 min_cai; + __le32 max_cai; + __le32 cai_step; +}; + +struct scmi_msg_powercap_get_v3 { + __le32 domain_id; + __le32 cpli; +}; + struct scmi_msg_powercap_set_cap_or_pai { - __le32 domain; + __le32 domain_id; __le32 flags; #define CAP_SET_ASYNC BIT(1) #define CAP_SET_IGNORE_DRESP BIT(0) __le32 value; }; =20 +struct scmi_msg_powercap_set_cap_v3 { + __le32 domain_id; + __le32 cpli; + __le32 flags; + __le32 power_cap; +}; + struct scmi_msg_resp_powercap_cap_set_complete { - __le32 domain; + __le32 domain_id; + __le32 power_cap; +}; + +struct scmi_msg_resp_powercap_cap_set_complete_v3 { + __le32 domain_id; __le32 power_cap; + __le32 cpli; }; =20 struct scmi_msg_resp_powercap_meas_get { @@ -112,6 +152,33 @@ struct scmi_powercap_meas_changed_notify_payld { __le32 power; }; =20 +struct scmi_msg_powercap_cpc { + __le32 domain_id; + __le32 desc_index; +}; + +struct scmi_msg_resp_powercap_cpc { + __le32 num_cpl; +#define NUM_RETURNED(n) (le32_get_bits((n), GENMASK(15, 0))) +#define NUM_REMAINING(n) (le32_get_bits((n), GENMASK(31, 16))) + struct { + __le32 cpli; + __le32 flags; + __le32 min_power_cap; + __le32 max_power_cap; + __le32 power_cap_step; + __le32 min_cai; + __le32 max_cai; + __le32 cai_step; + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; + } desc[]; +}; + +struct scmi_cpls_priv { + u32 domain_id; + struct scmi_powercap_cpl_info *cpli; +}; + struct scmi_powercap_state { bool enabled; u32 last_pcap; @@ -129,6 +196,11 @@ struct powercap_info { bool notify_measurements_cmd; struct scmi_powercap_state *states; struct scmi_powercap_info *powercaps; + int (*xfer_cap_get)(const struct scmi_protocol_handle *ph, + const struct scmi_powercap_info *pc, u32 cpl_id, u32 *power_cap); + int (*xfer_cap_set)(const struct scmi_protocol_handle *ph, + const struct scmi_powercap_info *pc, + u32 cpl_id, u32 power_cap, bool ignore_dresp); }; =20 static enum scmi_powercap_protocol_cmd evt_2_cmd[] =3D { @@ -192,18 +264,227 @@ scmi_powercap_validate(unsigned int min_val, unsigne= d int max_val, return 0; } =20 +static void iter_powercap_cpls_prepare_message(void *message, + unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_powercap_cpc *msg =3D message; + const struct scmi_cpls_priv *p =3D priv; + + msg->domain_id =3D cpu_to_le32(p->domain_id); + msg->desc_index =3D cpu_to_le32(desc_index); +} + +static int iter_powercap_cpls_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_msg_resp_powercap_cpc *r =3D response; + size_t needed; + + if (st->rx_len < sizeof(r->num_cpl)) + return -EPROTO; + + st->num_returned =3D NUM_RETURNED(r->num_cpl); + st->num_remaining =3D NUM_REMAINING(r->num_cpl); + + needed =3D struct_size(r, desc, st->num_returned); + if (st->rx_len < needed) + return -EPROTO; + + return 0; +} + +static int +iter_powercap_cpls_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + const struct scmi_msg_resp_powercap_cpc *r =3D response; + struct scmi_cpls_priv *p =3D priv; + struct scmi_powercap_cpl_info *cpl; + + cpl =3D &p->cpli[st->desc_index + st->loop_idx]; + + cpl->id =3D le32_to_cpu(r->desc[st->loop_idx].cpli); + cpl->cap_config =3D le32_to_cpu(r->desc[st->loop_idx].flags) & BIT(0); + + cpl->min_power_cap =3D le32_to_cpu(r->desc[st->loop_idx].min_power_cap); + cpl->max_power_cap =3D le32_to_cpu(r->desc[st->loop_idx].max_power_cap); + cpl->power_cap_step =3D le32_to_cpu(r->desc[st->loop_idx].power_cap_step); + if (!cpl->power_cap_step && cpl->min_power_cap !=3D cpl->max_power_cap) + return -EINVAL; + + cpl->min_avg_ivl =3D le32_to_cpu(r->desc[st->loop_idx].min_cai); + cpl->max_avg_ivl =3D le32_to_cpu(r->desc[st->loop_idx].max_cai); + cpl->avg_ivl_step =3D le32_to_cpu(r->desc[st->loop_idx].cai_step); + if (!cpl->avg_ivl_step && cpl->min_avg_ivl !=3D cpl->max_avg_ivl) + return -EINVAL; + + cpl->avg_ivl_config =3D cpl->min_avg_ivl !=3D cpl->max_avg_ivl; + + strscpy(cpl->name, r->desc[st->loop_idx].name, SCMI_SHORT_NAME_MAX_SIZE); + + return 0; +} + +static int scmi_powercap_cpls_enumerate(const struct scmi_protocol_handle = *ph, + struct scmi_powercap_info *dom_info) +{ + void *iter; + struct scmi_iterator_ops ops =3D { + .prepare_message =3D iter_powercap_cpls_prepare_message, + .update_state =3D iter_powercap_cpls_update_state, + .process_response =3D iter_powercap_cpls_process_response, + }; + struct scmi_cpls_priv cpriv =3D { + .domain_id =3D dom_info->id, + .cpli =3D dom_info->cpli, + }; + + iter =3D ph->hops->iter_response_init(ph, &ops, dom_info->num_cpli, + POWERCAP_CPC_ATTRIBUTES, + sizeof(struct scmi_msg_powercap_cpc), + &cpriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + return ph->hops->iter_response_run(iter); +} + +static int +scmi_powercap_domain_attrs_process(const struct scmi_protocol_handle *ph, + struct powercap_info *pinfo, + struct scmi_powercap_info *dom_info, void *r) +{ + struct scmi_msg_resp_powercap_domain_attributes *resp =3D r; + u32 flags =3D le32_to_cpu(resp->attributes); + bool cap_config; + int ret; + + cap_config =3D SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { + dom_info->num_cpli =3D 1; + } else { + dom_info->num_cpli =3D le32_get_bits(resp->attributes, + GENMASK(18, 15)); + + if (cap_config && !dom_info->num_cpli) + return -EINVAL; + + if (!dom_info->num_cpli) + dom_info->num_cpli =3D 1; + } + + dom_info->cpli =3D devm_kcalloc(ph->dev, dom_info->num_cpli, + sizeof(*dom_info->cpli), GFP_KERNEL); + if (!dom_info->cpli) + return -ENOMEM; + + if (pinfo->notify_cap_cmd) { + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) + dom_info->notify_powercap_cap_change =3D + SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); + else + dom_info->notify_powercap_cap_change =3D + SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(flags); + } + + if (pinfo->notify_measurements_cmd) + dom_info->notify_powercap_measurement_change =3D + SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); + + dom_info->extended_names =3D SUPPORTS_EXTENDED_NAMES(flags); + + dom_info->async_powercap_cap_set =3D + SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); + + dom_info->powercap_monitoring =3D + SUPPORTS_POWERCAP_MONITORING(flags); + dom_info->powercap_scale_mw =3D + SUPPORTS_POWER_UNITS_MW(flags); + dom_info->powercap_scale_uw =3D + SUPPORTS_POWER_UNITS_UW(flags); + dom_info->fastchannels =3D + SUPPORTS_POWERCAP_FASTCHANNELS(flags); + + strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); + + dom_info->sustainable_power =3D + le32_to_cpu(resp->sustainable_power); + dom_info->accuracy =3D le32_to_cpu(resp->accuracy); + + dom_info->parent_id =3D le32_to_cpu(resp->parent_id); + if (dom_info->parent_id !=3D SCMI_POWERCAP_ROOT_ZONE_ID && + (dom_info->parent_id >=3D pinfo->num_domains || + dom_info->parent_id =3D=3D dom_info->id)) { + dev_err(ph->dev, + "Platform reported inconsistent parent ID for domain %d - %s\n", + dom_info->id, dom_info->name); + return -ENODEV; + } + + dom_info->cpli[0].id =3D CPL0; + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) + dom_info->cpli[0].avg_ivl_config =3D + SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); + else + dom_info->cpli[0].avg_ivl_config =3D + SUPPORTS_POWERCAP_CAI_CONFIGURATION(flags); + + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { + dom_info->cpli[0].min_avg_ivl =3D le32_to_cpu(resp->min_pai); + dom_info->cpli[0].max_avg_ivl =3D le32_to_cpu(resp->max_pai); + dom_info->cpli[0].avg_ivl_step =3D le32_to_cpu(resp->pai_step); + } else { + struct scmi_msg_resp_powercap_domain_attributes_v3 *resp =3D r; + + dom_info->cpli[0].min_avg_ivl =3D le32_to_cpu(resp->min_cai); + dom_info->cpli[0].max_avg_ivl =3D le32_to_cpu(resp->max_cai); + dom_info->cpli[0].avg_ivl_step =3D le32_to_cpu(resp->cai_step); + } + + ret =3D scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl, + dom_info->cpli[0].max_avg_ivl, + dom_info->cpli[0].avg_ivl_step, + dom_info->cpli[0].avg_ivl_config); + if (ret) { + dev_err(ph->dev, + "Platform reported inconsistent PAI config for domain %d - %s\n", + dom_info->id, dom_info->name); + return ret; + } + + dom_info->cpli[0].cap_config =3D cap_config; + dom_info->cpli[0].min_power_cap =3D le32_to_cpu(resp->min_power_cap); + dom_info->cpli[0].max_power_cap =3D le32_to_cpu(resp->max_power_cap); + dom_info->cpli[0].power_cap_step =3D le32_to_cpu(resp->power_cap_step); + ret =3D scmi_powercap_validate(dom_info->cpli[0].min_power_cap, + dom_info->cpli[0].max_power_cap, + dom_info->cpli[0].power_cap_step, + dom_info->cpli[0].cap_config); + if (ret) { + dev_err(ph->dev, + "Platform reported inconsistent CAP config for domain %d - %s\n", + dom_info->id, dom_info->name); + return ret; + } + /* Just using same short name */ + strscpy(dom_info->cpli[0].name, dom_info->name, SCMI_SHORT_NAME_MAX_SIZE); + + return 0; +} + static int scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, struct powercap_info *pinfo, struct scmi_powercap_info *dom_info) { int ret; - u32 flags; struct scmi_xfer *t; struct scmi_msg_resp_powercap_domain_attributes *resp; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, - sizeof(dom_info->id), sizeof(*resp), &t); + sizeof(dom_info->id), 0, &t); if (ret) return ret; =20 @@ -212,91 +493,34 @@ scmi_powercap_domain_attributes_get(const struct scmi= _protocol_handle *ph, =20 ret =3D ph->xops->do_xfer(ph, t); if (!ret) { - flags =3D le32_to_cpu(resp->attributes); - - if (pinfo->notify_cap_cmd) - dom_info->notify_powercap_cap_change =3D - SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); - if (pinfo->notify_measurements_cmd) - dom_info->notify_powercap_measurement_change =3D - SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); - dom_info->async_powercap_cap_set =3D - SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); - - dom_info->powercap_monitoring =3D - SUPPORTS_POWERCAP_MONITORING(flags); - dom_info->powercap_scale_mw =3D - SUPPORTS_POWER_UNITS_MW(flags); - dom_info->powercap_scale_uw =3D - SUPPORTS_POWER_UNITS_UW(flags); - dom_info->fastchannels =3D - SUPPORTS_POWERCAP_FASTCHANNELS(flags); - - strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); - - dom_info->sustainable_power =3D - le32_to_cpu(resp->sustainable_power); - dom_info->accuracy =3D le32_to_cpu(resp->accuracy); - - dom_info->parent_id =3D le32_to_cpu(resp->parent_id); - if (dom_info->parent_id !=3D SCMI_POWERCAP_ROOT_ZONE_ID && - (dom_info->parent_id >=3D pinfo->num_domains || - dom_info->parent_id =3D=3D dom_info->id)) { - dev_err(ph->dev, - "Platform reported inconsistent parent ID for domain %d - %s\n", - dom_info->id, dom_info->name); - ret =3D -ENODEV; - } - - dom_info->cpli[0].avg_ivl_config =3D - SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); - dom_info->cpli[0].min_avg_ivl =3D le32_to_cpu(resp->min_pai); - dom_info->cpli[0].max_avg_ivl =3D le32_to_cpu(resp->max_pai); - dom_info->cpli[0].avg_ivl_step =3D le32_to_cpu(resp->pai_step); - ret =3D scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl, - dom_info->cpli[0].max_avg_ivl, - dom_info->cpli[0].avg_ivl_step, - dom_info->cpli[0].avg_ivl_config); - if (ret) { - dev_err(ph->dev, - "Platform reported inconsistent PAI config for domain %d - %s\n", - dom_info->id, dom_info->name); - goto clean; - } + size_t min_len; =20 - dom_info->cpli[0].cap_config =3D - SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); - dom_info->cpli[0].min_power_cap =3D le32_to_cpu(resp->min_power_cap); - dom_info->cpli[0].max_power_cap =3D le32_to_cpu(resp->max_power_cap); - dom_info->cpli[0].power_cap_step =3D le32_to_cpu(resp->power_cap_step); - ret =3D scmi_powercap_validate(dom_info->cpli[0].min_power_cap, - dom_info->cpli[0].max_power_cap, - dom_info->cpli[0].power_cap_step, - dom_info->cpli[0].cap_config); - if (ret) { - dev_err(ph->dev, - "Platform reported inconsistent CAP config for domain %d - %s\n", - dom_info->id, dom_info->name); - goto clean; - } + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) + min_len =3D sizeof(struct scmi_msg_resp_powercap_domain_attributes_v3); + else + min_len =3D sizeof(struct scmi_msg_resp_powercap_domain_attributes); =20 - /* Just using same short name */ - strscpy(dom_info->cpli[0].name, dom_info->name, - SCMI_SHORT_NAME_MAX_SIZE); + if (t->rx.len < min_len) + ret =3D -EPROTO; + else + ret =3D scmi_powercap_domain_attrs_process(ph, pinfo, dom_info, resp); } =20 -clean: ph->xops->xfer_put(ph, t); =20 /* * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) + if (!ret && dom_info->extended_names) ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, dom_info->id, NULL, dom_info->name, SCMI_MAX_STR_SIZE); =20 + /* When protocol version > 0x3 there can possibly be more than 1 CPLs */ + if (!ret && dom_info->num_cpli > 1) + ret =3D scmi_powercap_cpls_enumerate(ph, dom_info); + return ret; } =20 @@ -306,14 +530,7 @@ scmi_powercap_domain_initialize(const struct scmi_prot= ocol_handle *ph, { struct scmi_powercap_info *dom_info =3D pinfo->powercaps + domain; =20 - dom_info->num_cpli =3D 1; - dom_info->cpli =3D devm_kcalloc(ph->dev, dom_info->num_cpli, - sizeof(*dom_info->cpli), GFP_KERNEL); - if (!dom_info->cpli) - return -ENOMEM; - dom_info->id =3D domain; - dom_info->cpli[0].id =3D CPL0; =20 return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info); } @@ -337,7 +554,8 @@ scmi_powercap_dom_info_get(const struct scmi_protocol_h= andle *ph, u32 domain_id) } =20 static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *p= h, - u32 domain_id, u32 *power_cap) + const struct scmi_powercap_info *pc, u32 cpl_id, + u32 *power_cap) { int ret; struct scmi_xfer *t; @@ -347,7 +565,34 @@ static int scmi_powercap_xfer_cap_get(const struct scm= i_protocol_handle *ph, if (ret) return ret; =20 - put_unaligned_le32(domain_id, t->tx.buf); + put_unaligned_le32(pc->id, t->tx.buf); + + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) + *power_cap =3D get_unaligned_le32(t->rx.buf); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_powercap_xfer_cap_get_v3(const struct scmi_protocol_handle= *ph, + const struct scmi_powercap_info *pc, u32 cpl_id, + u32 *power_cap) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_powercap_get_v3 *msg; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg), + sizeof(u32), &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->domain_id =3D cpu_to_le32(pc->id); + msg->cpli =3D cpu_to_le32(pc->cpli[cpl_id].id); + ret =3D ph->xops->do_xfer(ph, t); if (!ret) *power_cap =3D get_unaligned_le32(t->rx.buf); @@ -361,6 +606,11 @@ static int __scmi_powercap_cap_get(const struct scmi_p= rotocol_handle *ph, const struct scmi_powercap_info *dom, u32 cpl_id, u32 *power_cap) { + struct powercap_info *pi =3D ph->get_priv(ph); + + if (cpl_id >=3D dom->num_cpli) + return -EINVAL; + if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) { *power_cap =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_a= ddr); @@ -369,7 +619,7 @@ static int __scmi_powercap_cap_get(const struct scmi_pr= otocol_handle *ph, return 0; } =20 - return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap); + return pi->xfer_cap_get(ph, dom, cpl_id, power_cap); } =20 static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, @@ -402,7 +652,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi= _protocol_handle *ph, return ret; =20 msg =3D t->tx.buf; - msg->domain =3D cpu_to_le32(pc->id); + msg->domain_id =3D cpu_to_le32(pc->id); msg->flags =3D cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) | FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp)); @@ -416,7 +666,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi= _protocol_handle *ph, struct scmi_msg_resp_powercap_cap_set_complete *resp; =20 resp =3D t->rx.buf; - if (le32_to_cpu(resp->domain) =3D=3D pc->id) + if (le32_to_cpu(resp->domain_id) =3D=3D pc->id) dev_dbg(ph->dev, "Powercap ID %d CAP set async to %u\n", pc->id, @@ -430,6 +680,51 @@ static int scmi_powercap_xfer_cap_set(const struct scm= i_protocol_handle *ph, return ret; } =20 +static int scmi_powercap_xfer_cap_set_v3(const struct scmi_protocol_handle= *ph, + const struct scmi_powercap_info *pc, + u32 cpl_id, u32 power_cap, + bool ignore_dresp) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_powercap_set_cap_v3 *msg; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, + sizeof(*msg), 0, &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->domain_id =3D cpu_to_le32(pc->id); + msg->cpli =3D cpu_to_le32(pc->cpli[cpl_id].id); + msg->flags =3D + cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) | + FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp)); + msg->power_cap =3D cpu_to_le32(power_cap); + + if (!pc->async_powercap_cap_set || ignore_dresp) { + ret =3D ph->xops->do_xfer(ph, t); + } else { + ret =3D ph->xops->do_xfer_with_response(ph, t); + if (!ret) { + struct scmi_msg_resp_powercap_cap_set_complete_v3 *resp; + + resp =3D t->rx.buf; + if (le32_to_cpu(resp->domain_id) =3D=3D pc->id && + le32_to_cpu(resp->cpli) =3D=3D pc->cpli[cpl_id].id) + dev_dbg(ph->dev, + "Powercap ID:%d/CPLI:%d CAP set async to %u\n", + pc->id, cpl_id, + get_unaligned_le32(&resp->power_cap)); + else + ret =3D -EPROTO; + } + } + + ph->xops->xfer_put(ph, t); + return ret; +} + static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, struct powercap_info *pi, u32 domain_id, u32 cpl_id, u32 power_cap, bool ignore_dresp) @@ -438,7 +733,7 @@ static int __scmi_powercap_cap_set(const struct scmi_pr= otocol_handle *ph, const struct scmi_powercap_info *pc; =20 pc =3D scmi_powercap_dom_info_get(ph, domain_id); - if (!pc || !pc->cpli[cpl_id].cap_config) + if (!pc || cpl_id >=3D pc->num_cpli || !pc->cpli[cpl_id].cap_config) return ret; =20 if (power_cap && @@ -456,12 +751,12 @@ static int __scmi_powercap_cap_set(const struct scmi_= protocol_handle *ph, domain_id, power_cap, 0); ret =3D 0; } else { - ret =3D scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap, - ignore_dresp); + ret =3D pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp); } =20 - /* Save the last explicitly set non-zero powercap value */ - if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2 && !ret && power_cap) + /* Save the last explicitly set non-zero powercap value for CPL0 */ + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2 && !ret && + cpl_id =3D=3D CPL0 && power_cap) pi->states[domain_id].last_pcap =3D power_cap; =20 return ret; @@ -480,8 +775,8 @@ static int scmi_powercap_cap_set(const struct scmi_prot= ocol_handle *ph, if (!power_cap) return -EINVAL; =20 - /* Just log the last set request if acting on a disabled domain */ - if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2) { + /* Just log the last set request on CPL0 if acting on a disabled domain */ + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2 && cpl_id =3D=3D CPL0) { if (!scmi_powercap_dom_info_get(ph, domain_id)) return -EINVAL; =20 @@ -558,7 +853,7 @@ static int scmi_powercap_xfer_pai_set(const struct scmi= _protocol_handle *ph, return ret; =20 msg =3D t->tx.buf; - msg->domain =3D cpu_to_le32(domain_id); + msg->domain_id =3D cpu_to_le32(domain_id); msg->flags =3D cpu_to_le32(0); msg->value =3D cpu_to_le32(pai); =20 @@ -1023,6 +1318,16 @@ scmi_powercap_protocol_init(const struct scmi_protoc= ol_handle *ph) if (!pinfo) return -ENOMEM; =20 + ph->set_priv(ph, pinfo); + + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { + pinfo->xfer_cap_get =3D scmi_powercap_xfer_cap_get; + pinfo->xfer_cap_set =3D scmi_powercap_xfer_cap_set; + } else { + pinfo->xfer_cap_get =3D scmi_powercap_xfer_cap_get_v3; + pinfo->xfer_cap_set =3D scmi_powercap_xfer_cap_set_v3; + } + ret =3D scmi_powercap_attributes_get(ph, pinfo); if (ret) return ret; diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index 7c2dbba5002c..2d9370aaaf5d 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -96,7 +96,7 @@ scmi_powercap_set_root_children_enable_state(struct scmi_= powercap_root *pr, bool if (!scmi_powercap_is_control_type_child(spz)) continue; =20 - if (!spz->info->powercap_cap_config) + if (!spz->info->cpli[0].cap_config) continue; =20 ret =3D powercap_ops->cap_enable_get(spz->ph, spz->info->id, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index ecd99b24ae92..8b539bfcf482 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -677,6 +677,7 @@ struct scmi_powercap_info { bool powercap_monitoring; bool powercap_scale_mw; bool powercap_scale_uw; + bool extended_names; bool fastchannels; char name[SCMI_MAX_STR_SIZE]; unsigned int sustainable_power; --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A37F15AA67B; Wed, 9 Sep 2026 22:05:01 +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=1788991504; cv=none; b=Agwg8zlKuY+Dbm2L++DAWdLve7RZno+uRgwUfE69nLV/Ai6cQLa4PovRhvjgrnYTDN3C+HQAoaukq/NtkhSycFGSx+ZCsDhPhmeGzC+R08D0BSv+HAZkxXyBdQQ4CRad+zlf5GEzNEQbtO8zvV436yDn7ZJ6HJSEfO3jsaPTuus= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991504; c=relaxed/simple; bh=5U3SLxrW4yEv6A5K4sAoFese4+FFrzRTFFJZJZTRCrk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XCUHtIUlpXXtnW16frl3mSd8Ik5oBLCaumLC8nNaU2XfwcO3jqHUFVzl/5ll6a3zKvwVzXp+/Ik3MEvBgIwDM/z/5l++KO2HbkzypdN7ZAPemkoCX0MTDxfD3RVxVCQOsNJJiZ+16rIlawjejIOr94OiPpt01o7ISfk+PC+nwHg= 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=pvRmRW5E; 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="pvRmRW5E" 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 5D7FA176B; Wed, 9 Sep 2026 15:04:57 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C45933F7D8; Wed, 9 Sep 2026 15:04:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991500; bh=5U3SLxrW4yEv6A5K4sAoFese4+FFrzRTFFJZJZTRCrk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pvRmRW5EW9+sdud9F3LaHYj5bPU2XSB6aBlBo0glOVv09kiD2VI695X63HPsGskxl kRgueu3DdKe/qWFWo0TN69TSHcF1E/g2Taan1agztH+/I0lP64k3D38RReBAYCJdda C2rglXBA0fiI8e9lZRhOAnKXLdYuzahKzjKbQpE8= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 05/13] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support Date: Wed, 9 Sep 2026 22:04:29 +0000 Message-Id: <20260909220437.1254412-6-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Add support for new SCMIv4.0 Powercap Fastchannels. Signed-off-by: Cristian Marussi [Philip: removed reference to old versioning logic] Signed-off-by: Philip Radford --- drivers/firmware/arm_scmi/powercap.c | 331 ++++++++++++++++++--------- 1 file changed, 229 insertions(+), 102 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 7ee39434a4db..a9566341682c 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -27,19 +27,25 @@ enum scmi_powercap_protocol_cmd { POWERCAP_CAP_GET =3D 0x4, POWERCAP_CAP_SET =3D 0x5, POWERCAP_PAI_GET =3D 0x6, + POWERCAP_MAI_GET =3D POWERCAP_PAI_GET, POWERCAP_PAI_SET =3D 0x7, + POWERCAP_MAI_SET =3D POWERCAP_PAI_SET, POWERCAP_DOMAIN_NAME_GET =3D 0x8, POWERCAP_MEASUREMENTS_GET =3D 0x9, POWERCAP_CAP_NOTIFY =3D 0xa, POWERCAP_MEASUREMENTS_NOTIFY =3D 0xb, POWERCAP_DESCRIBE_FASTCHANNEL =3D 0xc, POWERCAP_CPC_ATTRIBUTES =3D 0xd, + POWERCAP_CAI_GET =3D 0xe, + POWERCAP_CAI_SET =3D 0xf, }; =20 enum { POWERCAP_FC_CAP, - POWERCAP_FC_PAI, - POWERCAP_FC_MAX, + POWERCAP_FC_XAI, + POWERCAP_FC_MAI, + POWERCAP_FC_MEASUREMENT, + POWERCAP_FC_MAX }; =20 struct scmi_msg_resp_powercap_domain_attributes { @@ -91,12 +97,12 @@ struct scmi_msg_resp_powercap_domain_attributes_v3 { __le32 cai_step; }; =20 -struct scmi_msg_powercap_get_v3 { +struct scmi_msg_powercap_cap_or_cai_get_v3 { __le32 domain_id; __le32 cpli; }; =20 -struct scmi_msg_powercap_set_cap_or_pai { +struct scmi_msg_powercap_cap_or_pai_set { __le32 domain_id; __le32 flags; #define CAP_SET_ASYNC BIT(1) @@ -104,13 +110,20 @@ struct scmi_msg_powercap_set_cap_or_pai { __le32 value; }; =20 -struct scmi_msg_powercap_set_cap_v3 { +struct scmi_msg_powercap_cap_set_v3 { __le32 domain_id; __le32 cpli; __le32 flags; __le32 power_cap; }; =20 +struct scmi_msg_powercap_cai_set { + __le32 domain_id; + __le32 flags; + __le32 cai; + __le32 cpli; +}; + struct scmi_msg_resp_powercap_cap_set_complete { __le32 domain_id; __le32 power_cap; @@ -201,6 +214,10 @@ struct powercap_info { int (*xfer_cap_set)(const struct scmi_protocol_handle *ph, const struct scmi_powercap_info *pc, u32 cpl_id, u32 power_cap, bool ignore_dresp); + int (*xfer_avg_ivl_get)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 *ivl); + int (*xfer_avg_ivl_set)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 ivl); }; =20 static enum scmi_powercap_protocol_cmd evt_2_cmd[] =3D { @@ -524,17 +541,6 @@ scmi_powercap_domain_attributes_get(const struct scmi_= protocol_handle *ph, return ret; } =20 -static int -scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph, - struct powercap_info *pinfo, u32 domain) -{ - struct scmi_powercap_info *dom_info =3D pinfo->powercaps + domain; - - dom_info->id =3D domain; - - return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info); -} - static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle= *ph) { struct powercap_info *pi =3D ph->get_priv(ph); @@ -582,7 +588,7 @@ static int scmi_powercap_xfer_cap_get_v3(const struct s= cmi_protocol_handle *ph, { int ret; struct scmi_xfer *t; - struct scmi_msg_powercap_get_v3 *msg; + struct scmi_msg_powercap_cap_or_cai_get_v3 *msg; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg), sizeof(u32), &t); @@ -644,7 +650,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi= _protocol_handle *ph, { int ret; struct scmi_xfer *t; - struct scmi_msg_powercap_set_cap_or_pai *msg; + struct scmi_msg_powercap_cap_or_pai_set *msg; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, sizeof(*msg), 0, &t); @@ -687,7 +693,7 @@ static int scmi_powercap_xfer_cap_set_v3(const struct s= cmi_protocol_handle *ph, { int ret; struct scmi_xfer *t; - struct scmi_msg_powercap_set_cap_v3 *msg; + struct scmi_msg_powercap_cap_set_v3 *msg; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, sizeof(*msg), 0, &t); @@ -790,8 +796,9 @@ static int scmi_powercap_cap_set(const struct scmi_prot= ocol_handle *ph, power_cap, ignore_dresp); } =20 -static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *p= h, - u32 domain_id, u32 *pai) +static int +scmi_powercap_xfer_avg_interval_get(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 *ivl) { int ret; struct scmi_xfer *t; @@ -804,58 +811,105 @@ static int scmi_powercap_xfer_pai_get(const struct s= cmi_protocol_handle *ph, put_unaligned_le32(domain_id, t->tx.buf); ret =3D ph->xops->do_xfer(ph, t); if (!ret) - *pai =3D get_unaligned_le32(t->rx.buf); + *ivl =3D get_unaligned_le32(t->rx.buf); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int +scmi_powercap_xfer_avg_interval_get_v3(const struct scmi_protocol_handle *= ph, + u32 domain_id, u32 cpl_id, u32 *ivl) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_powercap_cap_or_cai_get_v3 *msg; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAI_GET, sizeof(*msg), + sizeof(u32), &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->domain_id =3D cpu_to_le32(domain_id); + msg->cpli =3D cpu_to_le32(cpl_id); + + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) + *ivl =3D get_unaligned_le32(t->rx.buf); =20 ph->xops->xfer_put(ph, t); =20 return ret; } =20 -static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 cpl_id, u32 *pai) +static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handl= e *ph, + u32 domain_id, u32 cpl_id, u32 *val) { struct scmi_powercap_info *dom; struct powercap_info *pi =3D ph->get_priv(ph); =20 - if (!pai || domain_id >=3D pi->num_domains) + if (!val || domain_id >=3D pi->num_domains) return -EINVAL; =20 dom =3D pi->powercaps + domain_id; if (cpl_id >=3D dom->num_cpli) return -EINVAL; =20 - if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PA= I].get_addr) { - *pai =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr); - trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET, - domain_id, *pai, 0); + if (dom->cpli[cpl_id].fc_info && + dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr) { + int trace_cmd =3D (PROTOCOL_REV_MAJOR(ph->version) < 0x3) ? + POWERCAP_PAI_GET : POWERCAP_CAI_GET; + + *val =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, *val, 0= ); return 0; } =20 - return scmi_powercap_xfer_pai_get(ph, domain_id, pai); + return pi->xfer_avg_ivl_get(ph, domain_id, cpl_id, val); } =20 -static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handl= e *ph, - u32 domain_id, u32 cpl_id, u32 *val) +static int +scmi_powercap_xfer_avg_interval_set(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 cpl_id, u32 ivl) { - return scmi_powercap_pai_get(ph, domain_id, cpl_id, val); + int ret; + struct scmi_xfer *t; + struct scmi_msg_powercap_cap_or_pai_set *msg; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, sizeof(*msg), 0, &t= ); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->domain_id =3D cpu_to_le32(domain_id); + msg->flags =3D cpu_to_le32(0); + msg->value =3D cpu_to_le32(ivl); + + ret =3D ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + return ret; } =20 -static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *p= h, - u32 domain_id, u32 pai) +static int +scmi_powercap_xfer_avg_interval_set_v3(const struct scmi_protocol_handle *= ph, + u32 domain_id, u32 cpl_id, u32 ivl) { int ret; struct scmi_xfer *t; - struct scmi_msg_powercap_set_cap_or_pai *msg; + struct scmi_msg_powercap_cai_set *msg; =20 - ret =3D ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, - sizeof(*msg), 0, &t); + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_CAI_SET, sizeof(*msg), 0, &t= ); if (ret) return ret; =20 msg =3D t->tx.buf; msg->domain_id =3D cpu_to_le32(domain_id); msg->flags =3D cpu_to_le32(0); - msg->value =3D cpu_to_le32(pai); + msg->cai =3D cpu_to_le32(ivl); + msg->cpli =3D cpu_to_le32(cpl_id); =20 ret =3D ph->xops->do_xfer(ph, t); =20 @@ -863,48 +917,42 @@ static int scmi_powercap_xfer_pai_set(const struct sc= mi_protocol_handle *ph, return ret; } =20 -static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 cpl_id, u32 pai) +static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handl= e *ph, + u32 domain_id, u32 cpl_id, u32 ivl) { const struct scmi_powercap_info *pc; + struct powercap_info *pi =3D ph->get_priv(ph); =20 pc =3D scmi_powercap_dom_info_get(ph, domain_id); if (!pc || cpl_id >=3D pc->num_cpli || !pc->cpli[cpl_id].avg_ivl_config || - !pai || pai < pc->cpli[cpl_id].min_avg_ivl || - pai > pc->cpli[cpl_id].max_avg_ivl) + !ivl || ivl < pc->cpli[cpl_id].min_avg_ivl || + ivl > pc->cpli[cpl_id].max_avg_ivl) return -EINVAL; =20 - if (pc->cpli[cpl_id].fc_info && pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI]= .set_addr) { - struct scmi_fc_info *fci =3D &pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI]; + /* Note that fc_info descriptors for any unsupported FC will be NULL */ + if (pc->cpli[cpl_id].fc_info && + pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].set_addr) { + int trace_cmd =3D (PROTOCOL_REV_MAJOR(ph->version) < 0x3) ? + POWERCAP_PAI_SET : POWERCAP_CAI_SET; + struct scmi_fc_info *fci =3D &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI]; =20 - trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET, - domain_id, pai, 0); - iowrite32(pai, fci->set_addr); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, ivl, 0); + iowrite32(ivl, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); return 0; } =20 - return scmi_powercap_xfer_pai_set(ph, domain_id, pai); + return pi->xfer_avg_ivl_set(ph, domain_id, cpl_id, ivl); } =20 -static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handl= e *ph, - u32 domain_id, u32 cpl_id, u32 val) -{ - return scmi_powercap_pai_set(ph, domain_id, cpl_id, val); -} - -static int scmi_powercap_measurements_get(const struct scmi_protocol_handl= e *ph, - u32 domain_id, u32 *average_power, - u32 *pai) +static int +scmi_powercap_xfer_measurements_get(const struct scmi_protocol_handle *ph, + const struct scmi_powercap_info *pc, + u32 *avg_power, u32 *avg_ivl) { int ret; struct scmi_xfer *t; struct scmi_msg_resp_powercap_meas_get *resp; - const struct scmi_powercap_info *pc; - - pc =3D scmi_powercap_dom_info_get(ph, domain_id); - if (!pc || !pc->powercap_monitoring || !pai || !average_power) - return -EINVAL; =20 ret =3D ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, sizeof(u32), sizeof(*resp), &t); @@ -912,17 +960,42 @@ static int scmi_powercap_measurements_get(const struc= t scmi_protocol_handle *ph, return ret; =20 resp =3D t->rx.buf; - put_unaligned_le32(domain_id, t->tx.buf); + put_unaligned_le32(pc->id, t->tx.buf); ret =3D ph->xops->do_xfer(ph, t); if (!ret) { - *average_power =3D le32_to_cpu(resp->power); - *pai =3D le32_to_cpu(resp->pai); + *avg_power =3D le32_to_cpu(resp->power); + *avg_ivl =3D le32_to_cpu(resp->pai); } =20 ph->xops->xfer_put(ph, t); return ret; } =20 +static int scmi_powercap_measurements_get(const struct scmi_protocol_handl= e *ph, + u32 domain_id, u32 *avg_power, + u32 *avg_ivl) +{ + const struct scmi_powercap_info *pc; + struct scmi_fc_info *fci; + + pc =3D scmi_powercap_dom_info_get(ph, domain_id); + if (!pc || !pc->powercap_monitoring || !avg_ivl || !avg_power) + return -EINVAL; + + /* Note that fc_info descriptors for any unsupported FC will be NULL */ + fci =3D pc->cpli[CPL0].fc_info; + if (fci && fci[POWERCAP_FC_MEASUREMENT].get_addr) { + *avg_power =3D ioread32(fci[POWERCAP_FC_MEASUREMENT].get_addr); + /* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */ + *avg_ivl =3D 0; + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MEASUREMENTS_GET, + pc->id, *avg_power, *avg_ivl); + return 0; + } + + return scmi_powercap_xfer_measurements_get(ph, pc, avg_power, avg_ivl); +} + static int scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle= *ph, u32 domain_id, u32 *power_thresh_low, @@ -1056,37 +1129,85 @@ static const struct scmi_powercap_proto_ops powerca= p_proto_ops =3D { }; =20 static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle= *ph, - u32 domain, struct scmi_fc_info **p_fc) + struct scmi_powercap_info *dom_info) { - struct scmi_fc_info *fc; - - fc =3D devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); - if (!fc) - return; - - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_CAP_SET, 4, domain, NULL, - &fc[POWERCAP_FC_CAP].set_addr, - &fc[POWERCAP_FC_CAP].set_db, - &fc[POWERCAP_FC_CAP].rate_limit); - - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_CAP_GET, 4, domain, NULL, - &fc[POWERCAP_FC_CAP].get_addr, NULL, - &fc[POWERCAP_FC_CAP].rate_limit); - - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_PAI_SET, 4, domain, NULL, - &fc[POWERCAP_FC_PAI].set_addr, - &fc[POWERCAP_FC_PAI].set_db, - &fc[POWERCAP_FC_PAI].rate_limit); - - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_PAI_GET, 4, domain, NULL, - &fc[POWERCAP_FC_PAI].get_addr, NULL, - &fc[POWERCAP_FC_PAI].rate_limit); - - *p_fc =3D fc; + struct scmi_fc_info *fc_cpl0; + + for (int id =3D 0; id < dom_info->num_cpli; id++) { + struct scmi_fc_info *fc; + u32 *cpl_id, zero_cpl_id =3D 0; + + fc =3D devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); + if (!fc) + return; + + /* NOTE THAT when num_cpli =3D=3D 1 the arg *cpl_id is 0 */ + cpl_id =3D (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) ? &id : NULL; + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_CAP_SET, 4, dom_info->id, + cpl_id, + &fc[POWERCAP_FC_CAP].set_addr, + &fc[POWERCAP_FC_CAP].set_db, + &fc[POWERCAP_FC_CAP].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_CAP_GET, 4, dom_info->id, + cpl_id, + &fc[POWERCAP_FC_CAP].get_addr, NULL, + &fc[POWERCAP_FC_CAP].rate_limit); + + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) { + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_CAI_SET, 4, + dom_info->id, cpl_id, + &fc[POWERCAP_FC_XAI].set_addr, + &fc[POWERCAP_FC_XAI].set_db, + &fc[POWERCAP_FC_XAI].rate_limit); + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_CAI_GET, 4, + dom_info->id, cpl_id, + &fc[POWERCAP_FC_XAI].get_addr, NULL, + &fc[POWERCAP_FC_XAI].rate_limit); + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MAI_SET, 4, + dom_info->id, &zero_cpl_id, + &fc[POWERCAP_FC_MAI].set_addr, + &fc[POWERCAP_FC_MAI].set_db, + &fc[POWERCAP_FC_MAI].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MAI_GET, 4, + dom_info->id, &zero_cpl_id, + &fc[POWERCAP_FC_MAI].get_addr, NULL, + &fc[POWERCAP_FC_MAI].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MEASUREMENTS_GET, 4, + dom_info->id, &zero_cpl_id, + &fc[POWERCAP_FC_MEASUREMENT].get_addr, NULL, + &fc[POWERCAP_FC_MEASUREMENT].rate_limit); + } + + dom_info->cpli[id].fc_info =3D fc; + } + + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { + fc_cpl0 =3D dom_info->cpli[CPL0].fc_info; + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_PAI_SET, 4, + dom_info->id, NULL, + &fc_cpl0[POWERCAP_FC_XAI].set_addr, + &fc_cpl0[POWERCAP_FC_XAI].set_db, + &fc_cpl0[POWERCAP_FC_XAI].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_PAI_GET, 4, + dom_info->id, NULL, + &fc_cpl0[POWERCAP_FC_XAI].get_addr, NULL, + &fc_cpl0[POWERCAP_FC_XAI].rate_limit); + } + } =20 static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, @@ -1323,9 +1444,14 @@ scmi_powercap_protocol_init(const struct scmi_protoc= ol_handle *ph) if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { pinfo->xfer_cap_get =3D scmi_powercap_xfer_cap_get; pinfo->xfer_cap_set =3D scmi_powercap_xfer_cap_set; + pinfo->xfer_avg_ivl_get =3D scmi_powercap_xfer_avg_interval_get; + pinfo->xfer_avg_ivl_set =3D scmi_powercap_xfer_avg_interval_set; + } else { pinfo->xfer_cap_get =3D scmi_powercap_xfer_cap_get_v3; pinfo->xfer_cap_set =3D scmi_powercap_xfer_cap_set_v3; + pinfo->xfer_avg_ivl_get =3D scmi_powercap_xfer_avg_interval_get_v3; + pinfo->xfer_avg_ivl_set =3D scmi_powercap_xfer_avg_interval_set_v3; } =20 ret =3D scmi_powercap_attributes_get(ph, pinfo); @@ -1350,18 +1476,19 @@ scmi_powercap_protocol_init(const struct scmi_proto= col_handle *ph) * formed and correlated by sane parent-child relationship (if any). */ for (domain =3D 0; domain < pinfo->num_domains; domain++) { - ret =3D scmi_powercap_domain_initialize(ph, pinfo, domain); + struct scmi_powercap_info *dom_info =3D pinfo->powercaps + domain; + + dom_info->id =3D domain; + ret =3D scmi_powercap_domain_attributes_get(ph, pinfo, dom_info); if (ret) return ret; =20 - if (pinfo->powercaps[domain].fastchannels) - scmi_powercap_domain_init_fc(ph, domain, - &pinfo->powercaps[domain].cpli[CPL0].fc_info); + if (dom_info->fastchannels) + scmi_powercap_domain_init_fc(ph, dom_info); =20 /* Grab initial state when disable is supported. */ if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x2) { - ret =3D __scmi_powercap_cap_get(ph, - &pinfo->powercaps[domain], CPL0, + ret =3D __scmi_powercap_cap_get(ph, dom_info, CPL0, &pinfo->states[domain].last_pcap); if (ret) return ret; --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D800045D905; Wed, 9 Sep 2026 22:05:03 +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=1788991506; cv=none; b=CvxpjdEDlOLE+hdvEoLT+PxfQIk4ZCZ5LLc6qWAMxDdJFsPTkMAS35yUdy2xM8JxLwQySuTCS4bvsFE26rrPZKIh1mD+IIYV2opZFtkxsGs59PO5sgewTFzFj0WtcCuqIHOkT5qdjiS9gL4faAlsWXP88XM1aizkm/jXgsiMt4Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991506; c=relaxed/simple; bh=BYeCQt5NaEq8E1gBj8dBtfs3f4F4ZCZN9kmhY/dO47I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sW3Vs+NlBXbECBP47TFlngqcV4hW0IzOcOZnTaGiFDKwRtOMorVc3m20WipLujPSzxLQSSRnFkM/jCD9Ka7hez7fZOciD7NWk0jJ6MMiD0k9E44o/mB3ZlZkx69rXEu9wg5Nr9hXi2ggCEfWCgkdfp3u3jhpEuO1JcgX3QpT9PE= 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=JIadh7gF; 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="JIadh7gF" 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 BEE10169C; Wed, 9 Sep 2026 15:04:59 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 327E53F7D8; Wed, 9 Sep 2026 15:05:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991503; bh=BYeCQt5NaEq8E1gBj8dBtfs3f4F4ZCZN9kmhY/dO47I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JIadh7gFST135EYcJ9nQnB2GhFdoyplnHNNVKGK611elZU3xTcGKlWwRUwjEQW0ql Abz1wa44MrMJarh7Ji2yUyEkCUZHP2lak9CV5Ao9wwHjMmQQ5k0hYnABd0W4/eKhHN pqverSjTcPDDf3WSswaIC01cM+QKRRKRSqZrvHFQ= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 06/13] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support Date: Wed, 9 Sep 2026 22:04:30 +0000 Message-Id: <20260909220437.1254412-7-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Extend notification support to the new SCMIv4.0 Powercap format that carry also a CPL identifier where specified. Since this addition completes SCMIv4.0 Powercap support bump also the protocol version define. Signed-off-by: Cristian Marussi Signed-off-by: Philip Radford --- V8->V9 - fixed CAP_CHANGED notification paring to accept old payload without cpli - changed secondary CPL validation to use scmi_powercap_validate to catch zero steps when min and max differ --- drivers/firmware/arm_scmi/powercap.c | 37 +++++++++++++++++++++------- include/linux/scmi_protocol.h | 3 ++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index a9566341682c..0357784a1e8d 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -18,7 +18,7 @@ #include "notify.h" =20 /* Updated only after ALL the mandatory features for that version are merg= ed */ -#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000 +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000 =20 #define CPL0 0 =20 @@ -156,7 +156,8 @@ struct scmi_powercap_cap_changed_notify_payld { __le32 agent_id; __le32 domain_id; __le32 power_cap; - __le32 pai; + __le32 avg_ivl; + __le32 cpli; }; =20 struct scmi_powercap_meas_changed_notify_payld { @@ -319,6 +320,7 @@ iter_powercap_cpls_process_response(const struct scmi_p= rotocol_handle *ph, const struct scmi_msg_resp_powercap_cpc *r =3D response; struct scmi_cpls_priv *p =3D priv; struct scmi_powercap_cpl_info *cpl; + int ret; =20 cpl =3D &p->cpli[st->desc_index + st->loop_idx]; =20 @@ -328,17 +330,28 @@ iter_powercap_cpls_process_response(const struct scmi= _protocol_handle *ph, cpl->min_power_cap =3D le32_to_cpu(r->desc[st->loop_idx].min_power_cap); cpl->max_power_cap =3D le32_to_cpu(r->desc[st->loop_idx].max_power_cap); cpl->power_cap_step =3D le32_to_cpu(r->desc[st->loop_idx].power_cap_step); - if (!cpl->power_cap_step && cpl->min_power_cap !=3D cpl->max_power_cap) - return -EINVAL; + + ret =3D scmi_powercap_validate(cpl->min_power_cap, + cpl->max_power_cap, + cpl->power_cap_step, + cpl->cap_config); + + if (ret) + return ret; =20 cpl->min_avg_ivl =3D le32_to_cpu(r->desc[st->loop_idx].min_cai); cpl->max_avg_ivl =3D le32_to_cpu(r->desc[st->loop_idx].max_cai); cpl->avg_ivl_step =3D le32_to_cpu(r->desc[st->loop_idx].cai_step); - if (!cpl->avg_ivl_step && cpl->min_avg_ivl !=3D cpl->max_avg_ivl) - return -EINVAL; - cpl->avg_ivl_config =3D cpl->min_avg_ivl !=3D cpl->max_avg_ivl; =20 + ret =3D scmi_powercap_validate(cpl->min_avg_ivl, + cpl->max_avg_ivl, + cpl->avg_ivl_step, + cpl->avg_ivl_config); + + if (ret) + return ret; + strscpy(cpl->name, r->desc[st->loop_idx].name, SCMI_SHORT_NAME_MAX_SIZE); =20 return 0; @@ -1349,14 +1362,20 @@ scmi_powercap_fill_custom_report(const struct scmi_= protocol_handle *ph, const struct scmi_powercap_cap_changed_notify_payld *p =3D payld; struct scmi_powercap_cap_changed_report *r =3D report; =20 - if (sizeof(*p) !=3D payld_sz) + if (payld_sz < offsetofend(struct scmi_powercap_cap_changed_notify_payld, + avg_ivl)) break; =20 r->timestamp =3D timestamp; r->agent_id =3D le32_to_cpu(p->agent_id); r->domain_id =3D le32_to_cpu(p->domain_id); r->power_cap =3D le32_to_cpu(p->power_cap); - r->pai =3D le32_to_cpu(p->pai); + r->avg_ivl =3D le32_to_cpu(p->avg_ivl); + if (payld_sz >=3D offsetofend(struct scmi_powercap_cap_changed_notify_pa= yld, + cpli)) + r->cpli =3D le32_to_cpu(p->cpli); + else + r->cpli =3D 0; *src_id =3D r->domain_id; rep =3D r; break; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 8b539bfcf482..ce00106d2b65 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1129,7 +1129,8 @@ struct scmi_powercap_cap_changed_report { unsigned int agent_id; unsigned int domain_id; unsigned int power_cap; - unsigned int pai; + unsigned int avg_ivl; + unsigned int cpli; }; =20 struct scmi_powercap_meas_changed_report { --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9045A493D34; Wed, 9 Sep 2026 22:05:06 +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=1788991508; cv=none; b=LMS0C2xcwPZZxRbFLdkpPbZH6mga1LC9rQVckNKGe9La/IUhjvduwpMvu9SfMiJNG0o5JiMVYlivG4JVqSqJLxpRHhsI30tXkfO/3Pz0wkQE8N/yyx0c8yh/qUEed8neSwEvdeyusfcI3pjMVXo4XjDgSdE9RJ8nRzTMgSGEJig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991508; c=relaxed/simple; bh=AUmRRBG0p3l7xe1VGSaebjSPrd7WncpTGO59D2nhsYY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UJFswxSILjINrl+QGU5mMLO2BjmURJLnLHxrsZWUAknZYjKGQKnMrEdN/i8BXqA+uBHICMQ6u7UtMnYsWPOY52b6SB7SOZkP/u7B+80p2J6zAjnLOe+boLV6e4plZqyarijP6JoXWt9bmHvFXrb7RTsus5jxXAqfmh91J98vHak= 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=Q/dhNs1Y; 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="Q/dhNs1Y" 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 0F9BB176B; Wed, 9 Sep 2026 15:05:02 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 949DE3F7D8; Wed, 9 Sep 2026 15:05:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991505; bh=AUmRRBG0p3l7xe1VGSaebjSPrd7WncpTGO59D2nhsYY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Q/dhNs1YZkvYwakWRwUu+QSJrodOSlEEzb0bDry+wlhrWCq5AiHFIlSg/ZzK7JYAP j1uMwxtdXrjPJ9RP5fiKBDN5hq88YthCUrFb4pUifMK7T75JWOX11ohLdRxg6h/A0i p1BC5o/Pl4k1wPEOD5YbMQQrH1z9gcZ3CY8wAJ38= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 07/13] firmware: arm_scmi: Extend powercap report to include MAI Date: Wed, 9 Sep 2026 22:04:31 +0000 Message-Id: <20260909220437.1254412-8-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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" Extend scmi_powercap_meas_changed_report to include MAI change notifications. Signed-off-by: Philip Radford --- drivers/firmware/arm_scmi/powercap.c | 28 +++++++++++++++------------- include/linux/scmi_protocol.h | 1 + 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 0357784a1e8d..b032c8181458 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -11,6 +11,7 @@ #include #include #include +#include =20 #include =20 @@ -21,6 +22,8 @@ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000 =20 #define CPL0 0 +#define SZ_V3 (sizeof(struct scmi_powercap_meas_changed_notify_payld)) +#define SZ_V2 (SZ_V3 - sizeof(__le32)) =20 enum scmi_powercap_protocol_cmd { POWERCAP_DOMAIN_ATTRIBUTES =3D 0x3, @@ -164,6 +167,7 @@ struct scmi_powercap_meas_changed_notify_payld { __le32 agent_id; __le32 domain_id; __le32 power; + __le32 mai; }; =20 struct scmi_msg_powercap_cpc { @@ -1250,24 +1254,18 @@ static int scmi_powercap_notify(const struct scmi_p= rotocol_handle *ph, struct scmi_msg_powercap_notify_thresh *notify; =20 /* - * Note that we have to pick the most recently configured - * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY - * enable request and we fail, complaining, if no thresholds - * were ever set, since this is an indication the API has been - * used wrongly. + * Build the POWERCAP_MEASUREMENTS_NOTIFY enable request using the + * most recently configured thresholds. + * + * The absence of thresholds is not considered an error: + * notifications can still be generated to report MAI changes, even + * when low and high are set to zero. */ ret =3D scmi_powercap_measurements_threshold_get(ph, domain, &low, &high); if (ret) return ret; =20 - if (enable && !low && !high) { - dev_err(ph->dev, - "Invalid Measurements Notify thresholds: %u/%u\n", - low, high); - return -EINVAL; - } - ret =3D ph->xops->xfer_get_init(ph, message_id, sizeof(*notify), 0, &t); if (ret) @@ -1385,13 +1383,17 @@ scmi_powercap_fill_custom_report(const struct scmi_= protocol_handle *ph, const struct scmi_powercap_meas_changed_notify_payld *p =3D payld; struct scmi_powercap_meas_changed_report *r =3D report; =20 - if (sizeof(*p) !=3D payld_sz) + if (payld_sz !=3D SZ_V2 && payld_sz !=3D SZ_V3) break; =20 r->timestamp =3D timestamp; r->agent_id =3D le32_to_cpu(p->agent_id); r->domain_id =3D le32_to_cpu(p->domain_id); r->power =3D le32_to_cpu(p->power); + r->mai =3D 0; + if (payld_sz =3D=3D SZ_V3 && PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) + r->mai =3D le32_to_cpu(p->mai); + *src_id =3D r->domain_id; rep =3D r; break; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index ce00106d2b65..583122037bf6 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1138,5 +1138,6 @@ struct scmi_powercap_meas_changed_report { unsigned int agent_id; unsigned int domain_id; unsigned int power; + unsigned int mai; }; #endif /* _LINUX_SCMI_PROTOCOL_H */ --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B765D493D5D; Wed, 9 Sep 2026 22:05: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=1788991510; cv=none; b=tkWwoUufRXPj5/4ALOAwkaFzTIhiMeRc+37NpWOcHbEVj3ElLek5KPJn2QJX3dqFDnh8cDdCogA6aTNboS/rV3hABPRfCN92QRVj+e4fbEmorfAVf6JvnVPiFusm1C04oORSc4sLbytKTOY2IrME87h/7aRIAM7y+LgLq6aAuQA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991510; c=relaxed/simple; bh=DBc1CUKQsyLSaWL5KNbvl9X9PnkANQ3XRP/x4evCVDE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RsKXxzZ39FWavd2xS34czhkXc1sHSIyNIYK1nzJyYcmwituUtObYAhjfjn/QDDpbEGjOOj9wyp/rHGpHTlZbsxvYq67RIOswZgwy9Nbm6VOEIuN9npcFT7O9K2azEb4adu4wBERGOzVw1oKYeIDSLawQ7iSs6RI4ePgCa+rwTpI= 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=AUdkoCCu; 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="AUdkoCCu" 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 6F319169C; Wed, 9 Sep 2026 15:05:04 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id DB5F13F7D8; Wed, 9 Sep 2026 15:05:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991508; bh=DBc1CUKQsyLSaWL5KNbvl9X9PnkANQ3XRP/x4evCVDE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AUdkoCCu9KNMvqzZa1Zqq5MeWtvRDkwIb43YFtzPeg6CVzUAmQqUkl947WQY1GXp2 CoSG9YGnoC6R0GIFq01i/u28lz9j1Qkmv/eEHI6n7Sn50JDqqNLJbyHJSzIS8KoBOJ FRjo+6pDw1t1ESzBLGGP+d2JZuonZ5GLbnQkn8cM= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi Subject: [PATCH v9 08/13] include: trace: Add new parameter to trace_scmi_fc_call Date: Wed, 9 Sep 2026 22:04:32 +0000 Message-Id: <20260909220437.1254412-9-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Since SCMIv4.0 some of the supported Fastchannels can be configured using an additional parameter like CPL_ID or Capability_ID. Add equivalent support in the SCMI fastchannel traces to printout also such parameter and fix all the existent call sites. When such parameter is not used, it will simply show up as zero. Signed-off-by: Cristian Marussi Signed-off-by: Philip Radford --- drivers/firmware/arm_scmi/perf.c | 8 ++++---- drivers/firmware/arm_scmi/powercap.c | 12 +++++++----- include/trace/events/scmi.h | 12 +++++++----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/p= erf.c index 7f283f457e02..88d614e3184b 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -552,7 +552,7 @@ static int __scmi_perf_limits_set(const struct scmi_pro= tocol_handle *ph, struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; =20 trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET, - dom->id, min_perf, max_perf); + dom->id, 0, min_perf, max_perf); iowrite32(max_perf, fci->set_addr); iowrite32(min_perf, fci->set_addr + 4); ph->hops->fastchannel_db_ring(fci->set_db); @@ -636,7 +636,7 @@ static int __scmi_perf_limits_get(const struct scmi_pro= tocol_handle *ph, *max_perf =3D ioread32(fci->get_addr); *min_perf =3D ioread32(fci->get_addr + 4); trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET, - dom->id, *min_perf, *max_perf); + dom->id, 0, *min_perf, *max_perf); return 0; } =20 @@ -706,7 +706,7 @@ static int __scmi_perf_level_set(const struct scmi_prot= ocol_handle *ph, struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LEVEL]; =20 trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET, - dom->id, level, 0); + dom->id, 0, level, 0); iowrite32(level, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); return 0; @@ -769,7 +769,7 @@ static int __scmi_perf_level_get(const struct scmi_prot= ocol_handle *ph, if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) { *level =3D ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr); trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET, - dom->id, *level, 0); + dom->id, 0, *level, 0); return 0; } =20 diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index b032c8181458..2e30bc00f056 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -638,7 +638,7 @@ static int __scmi_powercap_cap_get(const struct scmi_pr= otocol_handle *ph, dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) { *power_cap =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_a= ddr); trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET, - dom->id, *power_cap, 0); + dom->id, cpl_id, *power_cap, 0); return 0; } =20 @@ -771,7 +771,7 @@ static int __scmi_powercap_cap_set(const struct scmi_pr= otocol_handle *ph, iowrite32(power_cap, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET, - domain_id, power_cap, 0); + domain_id, cpl_id, power_cap, 0); ret =3D 0; } else { ret =3D pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp); @@ -880,7 +880,8 @@ static int scmi_powercap_avg_interval_get(const struct = scmi_protocol_handle *ph, POWERCAP_PAI_GET : POWERCAP_CAI_GET; =20 *val =3D ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr); - trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, *val, 0= ); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, + cpl_id, *val, 0); return 0; } =20 @@ -953,7 +954,8 @@ static int scmi_powercap_avg_interval_set(const struct = scmi_protocol_handle *ph, POWERCAP_PAI_SET : POWERCAP_CAI_SET; struct scmi_fc_info *fci =3D &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI]; =20 - trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, ivl, 0); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, + cpl_id, ivl, 0); iowrite32(ivl, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); return 0; @@ -1006,7 +1008,7 @@ static int scmi_powercap_measurements_get(const struc= t scmi_protocol_handle *ph, /* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */ *avg_ivl =3D 0; trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MEASUREMENTS_GET, - pc->id, *avg_power, *avg_ivl); + pc->id, 0, *avg_power, *avg_ivl); return 0; } =20 diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h index 703b7bb68e44..b03da7323d04 100644 --- a/include/trace/events/scmi.h +++ b/include/trace/events/scmi.h @@ -10,13 +10,14 @@ #define TRACE_SCMI_MAX_TAG_LEN 6 =20 TRACE_EVENT(scmi_fc_call, - TP_PROTO(u8 protocol_id, u8 msg_id, u32 res_id, u32 val1, u32 val2), - TP_ARGS(protocol_id, msg_id, res_id, val1, val2), + TP_PROTO(u8 protocol_id, u8 msg_id, u32 res_id, u32 sub_id, u32 val1, u32= val2), + TP_ARGS(protocol_id, msg_id, res_id, sub_id, val1, val2), =20 TP_STRUCT__entry( __field(u8, protocol_id) __field(u8, msg_id) __field(u32, res_id) + __field(u32, sub_id) __field(u32, val1) __field(u32, val2) ), @@ -25,13 +26,14 @@ TRACE_EVENT(scmi_fc_call, __entry->protocol_id =3D protocol_id; __entry->msg_id =3D msg_id; __entry->res_id =3D res_id; + __entry->sub_id =3D sub_id; __entry->val1 =3D val1; __entry->val2 =3D val2; ), =20 - TP_printk("pt=3D%02X msg_id=3D%02X res_id:%u vals=3D%u:%u", - __entry->protocol_id, __entry->msg_id, - __entry->res_id, __entry->val1, __entry->val2) + TP_printk("pt=3D%02X msg_id=3D%02X res_id:%u sub_id:%u vals=3D%u:%u", + __entry->protocol_id, __entry->msg_id, + __entry->res_id, __entry->sub_id, __entry->val1, __entry->val2) ); =20 TRACE_EVENT(scmi_xfer_begin, --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5DAB74A6897; Wed, 9 Sep 2026 22:05:11 +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=1788991513; cv=none; b=VGmhr51EuKweUDcco0cFXJSNWlQ9RosyZKdLSyLSezR4tglw5hV/tBKcJBCWynsD4UuwQU0DC6kA48ZG8QZTjJ/uuiAACwKlSPdiGX8UsKW7psM3L6dlbRxQXt1ik0CsH9//UxutJaM/pwvXggusFFfKdmvJ4WBClD0eZoXTTOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991513; c=relaxed/simple; bh=aknyCKmZa6bn8Vc/kSGrTAa8/4N0iLTZTK5+nRuMgKU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tVna+92Bdpz8rzRoOQcIExs2vmRM70PWAEfG4+LwBpUn65HKHNIFbgoCZiYJYWIncFfOn03E5ksQOrACLAG8caxiRTgNo8wcILz5uIGO4lPp7IRmO3XXX76cSBxHOoPGU8nRtapCor0UHAFsZ2IWOOkjEg4aJTDl1+VYgseBhiI= 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=gplb/gyJ; 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="gplb/gyJ" 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 F21BD1D31; Wed, 9 Sep 2026 15:05:06 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4908E3F7D8; Wed, 9 Sep 2026 15:05:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991510; bh=aknyCKmZa6bn8Vc/kSGrTAa8/4N0iLTZTK5+nRuMgKU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gplb/gyJeohXIPitwZP2G3RjpNt5XEw/W5q73ElKR0adExkca+Fs7Yhsj5yVMIvls MeVGIiqdKhnRFCrEvlYiN4OXBJgN7kYFpRd+AnuRf6dI07fcJ6y71Ij3cJU0ZdHc6v PS7lY+TCP65/Tpsw6xv1cIK19qC7QcWutQj4UUcA= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com, Cristian Marussi , "Rafael J. Wysocki" Subject: [PATCH v9 09/13] powercap: arm_scmi: Enable multiple constraints support Date: Wed, 9 Sep 2026 22:04:33 +0000 Message-Id: <20260909220437.1254412-10-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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: Cristian Marussi Initialize the domains with all the discovered available constraints, making available multiple per-domain constraints when the platform has advertised support for multiple concurrent power limits. CC: "Rafael J. Wysocki" CC: linux-pm@vger.kernel.org Signed-off-by: Cristian Marussi [Philip: Amended Copyright] Signed-off-by: Philip Radford --- V8->V9 - Added validation check to make sure powercap domain doesn't have too many constraints --- drivers/powercap/arm_scmi_powercap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index 2d9370aaaf5d..bcd1ba0bde8c 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -2,7 +2,7 @@ /* * SCMI Powercap support. * - * Copyright (C) 2022 ARM Ltd. + * Copyright (C) 2022-2026 ARM Ltd. */ =20 #include @@ -491,7 +491,7 @@ static int scmi_powercap_register_zone(struct scmi_powe= rcap_root *pr, =20 z =3D powercap_register_zone(&spz->zone, scmi_top_pcntrl, spz->info->name, parent ? &parent->zone : NULL, - &zone_ops, 1, &constraint_ops); + &zone_ops, spz->info->num_cpli, &constraint_ops); if (!IS_ERR(z)) { spz->height =3D scmi_powercap_get_zone_height(spz); spz->registered =3D true; @@ -649,6 +649,13 @@ static int scmi_powercap_probe(struct scmi_device *sde= v) */ spz->info =3D powercap_ops->info_get(ph, i); =20 + if (spz->info->num_cpli > MAX_CONSTRAINTS_PER_ZONE) { + dev_err(dev, "Powercap domain %s has too many constraints: %u > %u\n", + spz->info->name, spz->info->num_cpli, + MAX_CONSTRAINTS_PER_ZONE); + return -EINVAL; + } + spz->dev =3D dev; spz->ph =3D ph; spz->spzones =3D pr->spzones; --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 82E194A68AE; Wed, 9 Sep 2026 22:05:13 +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=1788991515; cv=none; b=J4t7/kL2W/Rnj7kWhYJDuyvNdvhudxTxx9zZu3qeJuseXI3cTh1qjOURFjkZTOzwubxlEj0IlHNl+EYFyihuCISXrD70dvJosWT3Bupy0T3aERepIrpROfUB0KdIf/PqCiOfZsIHKwBwbdlDVYQjmvE/aAKCXe12cwJY37mWy9Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991515; c=relaxed/simple; bh=ef6477RX1SfbSDDZb7uxx5n4DKa8eFEJI2ut9zi+u/Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MEhdH7bjmY+KyYyHVRlekk9Mq7j+MWjcrW9Pq2kI3/HpUStwGK0Ko2aRrqH5RA/NXSHA85zcWgVqOUE5WEkIaqtj6ZcxSuVu5JdNSv3AQi3iJ+k+Eh0A6As7IqMJLeoflYPKninKcP6/axpfEe9PambJe7kwQD0hqD6tyZl4FO8= 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=H9Om7Jes; 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="H9Om7Jes" 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 42BD01F02; Wed, 9 Sep 2026 15:05:09 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C6B6A3F7D8; Wed, 9 Sep 2026 15:05:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991512; bh=ef6477RX1SfbSDDZb7uxx5n4DKa8eFEJI2ut9zi+u/Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H9Om7JesHCwfATNdzDlg8Qb+DhYNd9EtmOI4cYAb0ZBiMRUq+7/4z+74JJH/YlKKr Py07qOOmFtmF9M9MH+kaDh0RKHSo9rL1OjiorW8XnwQ6/KiUs4oDeXFBgAv2E0Grpz 7sRWduRz1eY3cqKvCvdS9R0QZnsmgq3Cy9P3f+i4= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 10/13] firmware: arm_scmi: add Powercap MAI get/set support Date: Wed, 9 Sep 2026 22:04:34 +0000 Message-Id: <20260909220437.1254412-11-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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 Power Measurement Averaging Interval (MAI) get and set operations to the SCMI powercap protocol driver. Extends scmi_powercap_info to store MAI configuration and implement MAI get/set via xfer and optional fast-channel support. Signed-off-by: Philip Radford --- V8->V9 - Separated out domain specific fastchannel initialisation from per-cpl loop - Amended per-cpl fast channels to use firmware cpl id instead of relying on num_cpli numbering --- drivers/firmware/arm_scmi/powercap.c | 175 ++++++++++++++++++++++++--- include/linux/scmi_protocol.h | 18 +++ 2 files changed, 173 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_sc= mi/powercap.c index 2e30bc00f056..7d426c56b9c1 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -427,6 +427,34 @@ scmi_powercap_domain_attrs_process(const struct scmi_p= rotocol_handle *ph, dom_info->notify_powercap_measurement_change =3D SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); =20 + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) { + struct scmi_msg_resp_powercap_domain_attributes_v3 *resp_v3 =3D r; + + flags =3D le32_to_cpu(resp_v3->attributes); + if (pinfo->notify_measurements_cmd) + dom_info->notify_powercap_measurement_change =3D + SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); + + dom_info->mai_config =3D SUPPORTS_POWERCAP_MAI_CONFIGURATION(flags); + dom_info->min_mai =3D le32_to_cpu(resp_v3->min_mai); + dom_info->max_mai =3D le32_to_cpu(resp_v3->max_mai); + dom_info->mai_step =3D le32_to_cpu(resp_v3->mai_step); + + if (dom_info->mai_config) { + ret =3D scmi_powercap_validate(dom_info->min_mai, + dom_info->max_mai, + dom_info->mai_step, + dom_info->mai_config); + + if (ret) { + dev_warn(ph->dev, "Platform reported invalid MAI config for domain %d = - %s\n", + dom_info->id, dom_info->name); + + return ret; + } + } + } + dom_info->extended_names =3D SUPPORTS_EXTENDED_NAMES(flags); =20 dom_info->async_powercap_cap_set =3D @@ -1133,6 +1161,103 @@ static int scmi_powercap_cap_enable_get(const struc= t scmi_protocol_handle *ph, return 0; } =20 +static int scmi_powercap_xfer_mai_get(const struct scmi_protocol_handle *p= h, + u32 domain_id, u32 *mai) +{ + int ret; + struct scmi_xfer *t; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_MAI_GET, sizeof(u32), + sizeof(u32), &t); + + if (ret) + return ret; + + put_unaligned_le32(domain_id, t->tx.buf); + + ret =3D ph->xops->do_xfer(ph, t); + if (!ret) + *mai =3D get_unaligned_le32(t->rx.buf); + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_powercap_xfer_mai_set(const struct scmi_protocol_handle *p= h, + u32 domain_id, u32 mai) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_powercap_cap_or_pai_set *msg; + + ret =3D ph->xops->xfer_get_init(ph, POWERCAP_MAI_SET, sizeof(*msg), + 0, &t); + if (ret) + return ret; + + msg =3D t->tx.buf; + msg->domain_id =3D cpu_to_le32(domain_id); + msg->flags =3D cpu_to_le32(0); + msg->value =3D cpu_to_le32(mai); + + ret =3D ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int +scmi_powercap_measurements_interval_get(const struct scmi_protocol_handle = *ph, + u32 domain_id, + u32 *val) +{ + const struct scmi_powercap_info *pc; + struct scmi_fc_info *fci; + + if (!val) + return -EINVAL; + + pc =3D scmi_powercap_dom_info_get(ph, domain_id); + if (!pc) + return -EINVAL; + + fci =3D pc->cpli[CPL0].fc_info; + if (fci && fci[POWERCAP_FC_MAI].get_addr) { + *val =3D ioread32(fci[POWERCAP_FC_MAI].get_addr); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MAI_GET, + domain_id, 0, *val, 0); + return 0; + } + + return scmi_powercap_xfer_mai_get(ph, domain_id, val); +} + +static int +scmi_powercap_measurements_interval_set(const struct scmi_protocol_handle = *ph, + u32 domain_id, + u32 val) +{ + const struct scmi_powercap_info *pc; + struct scmi_fc_info *fci; + + pc =3D scmi_powercap_dom_info_get(ph, domain_id); + if (!pc) + return -EINVAL; + + if (!pc->mai_config || !val || val < pc->min_mai || val > pc->max_mai) + return -EINVAL; + + fci =3D pc->cpli[CPL0].fc_info; + if (fci && fci[POWERCAP_FC_MAI].set_addr) { + iowrite32(val, fci[POWERCAP_FC_MAI].set_addr); + ph->hops->fastchannel_db_ring(fci[POWERCAP_FC_MAI].set_db); + trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MAI_SET, domain_id, = 0, val, 0); + return 0; + } + + return scmi_powercap_xfer_mai_set(ph, domain_id, val); +} + static const struct scmi_powercap_proto_ops powercap_proto_ops =3D { .num_domains_get =3D scmi_powercap_num_domains_get, .info_get =3D scmi_powercap_dom_info_get, @@ -1145,6 +1270,8 @@ static const struct scmi_powercap_proto_ops powercap_= proto_ops =3D { .measurements_get =3D scmi_powercap_measurements_get, .measurements_threshold_set =3D scmi_powercap_measurements_threshold_set, .measurements_threshold_get =3D scmi_powercap_measurements_threshold_get, + .measurements_interval_get =3D scmi_powercap_measurements_interval_get, + .measurements_interval_set =3D scmi_powercap_measurements_interval_set, }; =20 static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle= *ph, @@ -1154,14 +1281,14 @@ static void scmi_powercap_domain_init_fc(const stru= ct scmi_protocol_handle *ph, =20 for (int id =3D 0; id < dom_info->num_cpli; id++) { struct scmi_fc_info *fc; - u32 *cpl_id, zero_cpl_id =3D 0; + u32 *cpl_id; + u32 cpl_fw_id =3D dom_info->cpli[id].id; =20 fc =3D devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); if (!fc) return; =20 - /* NOTE THAT when num_cpli =3D=3D 1 the arg *cpl_id is 0 */ - cpl_id =3D (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) ? &id : NULL; + cpl_id =3D (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) ? &cpl_fw_id : NUL= L; =20 ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, POWERCAP_CAP_SET, 4, dom_info->id, @@ -1188,29 +1315,37 @@ static void scmi_powercap_domain_init_fc(const stru= ct scmi_protocol_handle *ph, dom_info->id, cpl_id, &fc[POWERCAP_FC_XAI].get_addr, NULL, &fc[POWERCAP_FC_XAI].rate_limit); - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_MAI_SET, 4, - dom_info->id, &zero_cpl_id, - &fc[POWERCAP_FC_MAI].set_addr, - &fc[POWERCAP_FC_MAI].set_db, - &fc[POWERCAP_FC_MAI].rate_limit); - - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_MAI_GET, 4, - dom_info->id, &zero_cpl_id, - &fc[POWERCAP_FC_MAI].get_addr, NULL, - &fc[POWERCAP_FC_MAI].rate_limit); =20 - ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, - POWERCAP_MEASUREMENTS_GET, 4, - dom_info->id, &zero_cpl_id, - &fc[POWERCAP_FC_MEASUREMENT].get_addr, NULL, - &fc[POWERCAP_FC_MEASUREMENT].rate_limit); } =20 dom_info->cpli[id].fc_info =3D fc; } =20 + if (PROTOCOL_REV_MAJOR(ph->version) >=3D 0x3) { + u32 zero_cpl_id =3D 0; + + fc_cpl0 =3D dom_info->cpli[CPL0].fc_info; + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MAI_SET, 4, + dom_info->id, &zero_cpl_id, + &fc_cpl0[POWERCAP_FC_MAI].set_addr, + &fc_cpl0[POWERCAP_FC_MAI].set_db, + &fc_cpl0[POWERCAP_FC_MAI].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MAI_GET, 4, + dom_info->id, &zero_cpl_id, + &fc_cpl0[POWERCAP_FC_MAI].get_addr, NULL, + &fc_cpl0[POWERCAP_FC_MAI].rate_limit); + + ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, + POWERCAP_MEASUREMENTS_GET, 4, + dom_info->id, &zero_cpl_id, + &fc_cpl0[POWERCAP_FC_MEASUREMENT].get_addr, NULL, + &fc_cpl0[POWERCAP_FC_MEASUREMENT].rate_limit); + } + if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) { fc_cpl0 =3D dom_info->cpli[CPL0].fc_info; ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 583122037bf6..eeb5e609e782 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -658,6 +658,12 @@ struct scmi_powercap_cpl_info { * reports power data on an abstract linear scale. * @extended_names: Support for long names. * @fastchannels: Support for at least one fastchannel, + * @mai_config: MAI configuration support. + * @min_mai: Minimum supported Power Measurement Averaging Interval in + * microseconds. + * @max_mai: Maximum supporte Power Measurement Averaging Interval in + microseconds. + * @mai_step: Step size between supported MAI values in microseconds. * @name: name assigned to the Powercap Domain by platform. * @sustainable_power: Maximum sustainable power consumption for this doma= in * under normal conditions. @@ -679,6 +685,10 @@ struct scmi_powercap_info { bool powercap_scale_uw; bool extended_names; bool fastchannels; + bool mai_config; + u32 min_mai; + u32 max_mai; + u32 mai_step; char name[SCMI_MAX_STR_SIZE]; unsigned int sustainable_power; unsigned int accuracy; @@ -737,6 +747,10 @@ struct scmi_powercap_info { * @measurements_threshold_get: get the currently configured low and high = power * thresholds used when registering callbacks for * notification POWERCAP_MEASUREMENTS_NOTIFY. + * @measurements_interval_get: get the current Power Measurement Averaging + * Interval (MAI) value for the specified domain. + * @measurements_interval_set: set the Power Measurement Averaging Interval + * (MAI) value for the specified domain. */ struct scmi_powercap_proto_ops { int (*num_domains_get)(const struct scmi_protocol_handle *ph); @@ -762,6 +776,10 @@ struct scmi_powercap_proto_ops { int (*measurements_threshold_get)(const struct scmi_protocol_handle *ph, u32 domain_id, u32 *power_thresh_low, u32 *power_thresh_high); + int (*measurements_interval_get)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 *val); + int (*measurements_interval_set)(const struct scmi_protocol_handle *ph, + u32 domain_id, u32 val); }; =20 enum scmi_pinctrl_selector_type { --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D7BD445D917; Wed, 9 Sep 2026 22:05:15 +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=1788991517; cv=none; b=OY5OcipvuFzpUPVNVQ/Cle/awhlyTjncTpcEHE2/7XD4RsRmYm17nsSdzp3xDrDYQog9pGtcQvFAoPHWFtAE5sR95veHPSz9LPUhxYeh51aR7gDBPAg3uOO0DaYvEbCiQSm8MH8t4ee0VMMFZAp02uFzBFCxxVMV1/U4rfr6FQU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991517; c=relaxed/simple; bh=ovY3D17i0dzC62cyd/v0yJIlVY4EtK0/HjoZ47MH1Xc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=WPfDdUnXIMFdEQcxAQgz2MDHXM8DSVEiyu6E14/S+T3sY9bO9MN+P8Taj4dkFilnUehHJz9AHSb6m/hX9MKe4NrzzprSKVtIwbD0h9ACTJdB1Q/5nhNWZCp9RkpuPCmS8t17tcl8CN/qVRGVP0xfBPl05PqbeNULUT5cTzFMIHo= 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=SenWt2di; 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="SenWt2di" 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 88A79169C; Wed, 9 Sep 2026 15:05:11 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 18D433F7D8; Wed, 9 Sep 2026 15:05:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991515; bh=ovY3D17i0dzC62cyd/v0yJIlVY4EtK0/HjoZ47MH1Xc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SenWt2diybd3TLYBfG9DVki/PK1URuv757wNGaKuBsoiZ8ft9KhLUN4O3YfuLgIG+ YdnoTd1UdgqeaPsMfW/Aq85Xp1Zh2JZM7BX/RoJRzwwT/jXMf16Enr8fwC0qzbmghP khyHAO4LhvFNdIxeNYG9hxrBLfDiNwBQktV9R/yI= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 11/13] powercap: arm_scmi: Create synthetic parent node for multi-instance Date: Wed, 9 Sep 2026 22:04:35 +0000 Message-Id: <20260909220437.1254412-12-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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" An SCMI powercap instance may expose a hierarchy of domains, or even a forest of multiple domain trees rooted at SCMI_POWERCAP_ROOT_ZONE_ID. Those hierarchies are valid within the namespace of a single SCMI instance. Currently, the powercap framework has no notion of SCMI instances. If root domains from multiple SCMI instances are registered directly under the same Linux powercap control type, the per-instance boundaries are lost and the resulting Linux hierarchy becomes a merge of otherwise independent SCMI topologies. Add a synthetic top-level powercap zone per SCMI instance and register that instance's SCMI root domains beneath it. This keeps each instance's SCMI hierarchy grouped together. Signed-off-by: Philip Radford --- V8->V9 - Removed redundant scmi_powercap_get_zone_height - Added scmi_powercap_parent_name helper - Replaced tiernary operator that checked empty parent->info->name --- drivers/powercap/arm_scmi_powercap.c | 110 +++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index bcd1ba0bde8c..b5397d7675e6 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -38,6 +38,7 @@ struct scmi_powercap_root { struct scmi_powercap_zone *spzones; struct list_head *registered_zones; struct list_head scmi_zones; + struct scmi_powercap_zone instance_root; }; =20 static LIST_HEAD(scmi_powercap_roots); @@ -443,29 +444,75 @@ static const struct powercap_zone_constraint_ops cons= traint_ops =3D { .get_name =3D scmi_powercap_get_name, }; =20 +/* + * Multi-instance constraints to meet driver requrements due to the fact + * that full zone semantics aren't available for the synthetic zone. + */ +static int instance_root_release(struct powercap_zone *pz) +{ + return 0; +} + +static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *v) +{ + *v =3D 0; + return 0; +} + +static int instance_root_set_constraint(struct powercap_zone *pz, int cid,= u64 v) +{ + return -EOPNOTSUPP; +} + +static int instance_root_get_constraint(struct powercap_zone *pz, int cid,= u64 *v) +{ + return -EOPNOTSUPP; +} + +static const struct powercap_zone_ops instance_root_ops =3D { + .get_max_power_range_uw =3D scmi_powercap_get_max_power_range_uw, + .get_power_uw =3D instance_root_get_power_uw, + .release =3D instance_root_release, +}; + +static const struct powercap_zone_constraint_ops instance_root_const_ops = =3D { + .set_power_limit_uw =3D instance_root_set_constraint, + .get_power_limit_uw =3D instance_root_get_constraint, + .set_time_window_us =3D instance_root_set_constraint, + .get_time_window_us =3D instance_root_get_constraint, +}; + static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *= pr) { int i; =20 /* Un-register children zones first starting from the leaves */ - for (i =3D pr->num_zones - 1; i >=3D 0; i--) { + for (i =3D pr->num_zones; i >=3D 0; i--) { if (!list_empty(&pr->registered_zones[i])) { struct scmi_powercap_zone *spz; =20 - list_for_each_entry(spz, &pr->registered_zones[i], node) + list_for_each_entry(spz, &pr->registered_zones[i], node) { + if (!spz->registered) + continue; + + spz->registered =3D false; powercap_unregister_zone(scmi_top_pcntrl, &spz->zone); + } } } } =20 -static inline unsigned int -scmi_powercap_get_zone_height(struct scmi_powercap_zone *spz) +static const char *scmi_powercap_parent_name(struct scmi_powercap_root *pr, + struct scmi_powercap_zone *parent) { - if (spz->info->parent_id =3D=3D SCMI_POWERCAP_ROOT_ZONE_ID) - return 0; + if (!parent) + return "ROOT"; =20 - return spz->spzones[spz->info->parent_id].height + 1; + if (parent =3D=3D &pr->instance_root) + return "INSTANCE ROOT"; + + return parent->info->name; } =20 static inline struct scmi_powercap_zone * @@ -493,11 +540,14 @@ static int scmi_powercap_register_zone(struct scmi_po= wercap_root *pr, parent ? &parent->zone : NULL, &zone_ops, spz->info->num_cpli, &constraint_ops); if (!IS_ERR(z)) { - spz->height =3D scmi_powercap_get_zone_height(spz); + if (parent) + spz->height =3D parent->height + 1; + else + spz->height =3D 0; spz->registered =3D true; list_move(&spz->node, &pr->registered_zones[spz->height]); dev_dbg(spz->dev, "Registered node %s - parent %s - height:%d\n", - spz->info->name, parent ? parent->info->name : "ROOT", + spz->info->name, scmi_powercap_parent_name(pr, parent), spz->height); } else { list_del(&spz->node); @@ -505,7 +555,7 @@ static int scmi_powercap_register_zone(struct scmi_powe= rcap_root *pr, dev_err(spz->dev, "Error registering node:%s - parent:%s - h:%d - ret:%d\n", spz->info->name, - parent ? parent->info->name : "ROOT", + scmi_powercap_parent_name(pr, parent), spz->height, ret); } =20 @@ -564,6 +614,8 @@ static int scmi_zones_register(struct device *dev, struct scmi_powercap_zone *parent; =20 parent =3D scmi_powercap_get_parent_zone(spz); + if (!parent) + parent =3D &pr->instance_root; if (parent && !parent->registered) { zones_stack[sp++] =3D spz; spz =3D parent; @@ -604,8 +656,11 @@ static int scmi_powercap_probe(struct scmi_device *sde= v) int ret, i; struct scmi_powercap_root *pr; struct scmi_powercap_zone *spz; + struct scmi_powercap_zone *ir; struct scmi_protocol_handle *ph; struct device *dev =3D &sdev->dev; + char *instance_name; + struct powercap_zone *z; =20 if (!sdev->handle) return -ENODEV; @@ -633,7 +688,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev) return -ENOMEM; =20 /* Allocate for worst possible scenario of maximum tree height. */ - pr->registered_zones =3D devm_kcalloc(dev, pr->num_zones, + pr->registered_zones =3D devm_kcalloc(dev, pr->num_zones + 1, sizeof(*pr->registered_zones), GFP_KERNEL); if (!pr->registered_zones) @@ -641,6 +696,9 @@ static int scmi_powercap_probe(struct scmi_device *sdev) =20 INIT_LIST_HEAD(&pr->scmi_zones); =20 + for (i =3D 0; i <=3D pr->num_zones; i++) + INIT_LIST_HEAD(&pr->registered_zones[i]); + for (i =3D 0, spz =3D pr->spzones; i < pr->num_zones; i++, spz++) { /* * Powercap domains are validate by the protocol layer, i.e. @@ -660,7 +718,6 @@ static int scmi_powercap_probe(struct scmi_device *sdev) spz->ph =3D ph; spz->spzones =3D pr->spzones; INIT_LIST_HEAD(&spz->node); - INIT_LIST_HEAD(&pr->registered_zones[i]); =20 list_add_tail(&spz->node, &pr->scmi_zones); /* @@ -678,19 +735,44 @@ static int scmi_powercap_probe(struct scmi_device *sd= ev) } } =20 + ir =3D &pr->instance_root; + ir->dev =3D dev; + INIT_LIST_HEAD(&ir->node); + instance_name =3D devm_kasprintf(dev, GFP_KERNEL, "instance_%s", dev_name= (dev)); + if (!instance_name) + return -ENOMEM; + + z =3D powercap_register_zone(&ir->zone, scmi_top_pcntrl, + instance_name, NULL, &instance_root_ops, 0, + &instance_root_const_ops); + + if (IS_ERR(z)) { + ret =3D PTR_ERR(z); + dev_err(dev, "Failed to register sysnthetic instance root: %d\n", ret); + return ret; + } + + ir->registered =3D true; + ir->height =3D 0; + list_add_tail(&ir->node, &pr->registered_zones[0]); + /* * Scan array of retrieved SCMI powercap domains and register them * recursively starting from the root domains. */ ret =3D scmi_zones_register(dev, pr); - if (ret) + if (ret) { + scmi_powercap_unregister_all_zones(pr); return ret; + } =20 INIT_LIST_HEAD(&pr->node); =20 ret =3D scmi_powercap_read_root_children_enable_state(pr, &pr->enabled); - if (ret) + if (ret) { + scmi_powercap_unregister_all_zones(pr); return ret; + } =20 mutex_lock(&scmi_powercap_roots_lock); list_add_tail(&pr->node, &scmi_powercap_roots); --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E7B364AB1A1; Wed, 9 Sep 2026 22:05:17 +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=1788991519; cv=none; b=VRx6iWMyqnrLpenbc83zSQNJG8qUuXx3YcPONd9ArM1qm9xH0cq0Q0EsJTs7Hj9E3bPaMRxNitoTbUVFjc3Q9soa03Ld6Ksd9e/y8+SRZoXi8lZ9e4eXyg+rnwZUV55FFrMBgRf6VLnsWsHKPS7K8rEQeSqRtA6WgyvSaNGnz0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991519; c=relaxed/simple; bh=yzdZeYTmEZftojUpT9D3ZlNs2/1bcHwNlWKDhmH6rjY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=W6u6yvQ3MyChpxwZmBHFfJWKuwTSqgKKd/xUHLWQf8TFPmEbx+yZ5HkX4JHD1i3qXNULRIrmblO+Lbw8A2Aw8/MqMqYemtshKAUM398PZC8I8gxT2cYONqNhtQHnI1WHlyo2XXF+UqP2o1fXCIzrX8Dtowtp0/hOtlg8IaFqH5k= 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=styYlpZ6; 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="styYlpZ6" 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 CEB7D1D31; Wed, 9 Sep 2026 15:05:13 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5F04E3F7D8; Wed, 9 Sep 2026 15:05:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991517; bh=yzdZeYTmEZftojUpT9D3ZlNs2/1bcHwNlWKDhmH6rjY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=styYlpZ6A25hcY3/Lug1In80sdVOazJ5jYSfhtg+Einh5vto7DMDCL3X8knill71h sh1xiSZNVwzfkHQJ2PTnJRDLV5KMU3yUnOfAeiT33cOctwnQx4hHdUst4MfStjM0pe i2/3CH2UvjDW5W5M0fk5wWEpYDSgEJykACJrseYo= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 12/13] powercap: arm_scmi: Add get_power_uw to synthetic node Date: Wed, 9 Sep 2026 22:04:36 +0000 Message-Id: <20260909220437.1254412-13-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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" Exposes the current power usage from the immediate children of the synthetic (root) powercap node. Iterates over pr->spzones and sums per-zone power. Signed-off-by: Philip Radford --- V8->V9 - Removed redundant if (!pr) check from instance_root_get_power_uw() - Stopped returning a partial sum if one child read fails --- drivers/powercap/arm_scmi_powercap.c | 34 ++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index b5397d7675e6..aac0b00490b2 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -17,6 +17,9 @@ #define to_scmi_powercap_zone(z) \ container_of(z, struct scmi_powercap_zone, zone) =20 +#define to_scmi_powercap_root(z) \ + container_of(z, struct scmi_powercap_root, instance_root.zone) + static const struct scmi_powercap_proto_ops *powercap_ops; =20 struct scmi_powercap_zone { @@ -453,9 +456,36 @@ static int instance_root_release(struct powercap_zone = *pz) return 0; } =20 -static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *v) +static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *power= _uw) { - *v =3D 0; + struct scmi_powercap_root *pr =3D to_scmi_powercap_root(pz); + struct scmi_powercap_zone *child; + + u64 p, acc =3D 0; + int i, ret; + + if (!pz || !power_uw) + return -EINVAL; + + for (i =3D 0; i < pr->num_zones; i++) { + child =3D &pr->spzones[i]; + + if (!child->registered || child->invalid) + continue; + + if (child->info->parent_id !=3D SCMI_POWERCAP_ROOT_ZONE_ID) + continue; + + ret =3D scmi_powercap_get_power_uw(&child->zone, &p); + if (ret) { + dev_dbg(child->dev, "Failed to read child power: %d\n", ret); + return ret; + } + + acc +=3D p; + } + + *power_uw =3D acc; return 0; } =20 --=20 2.25.1 From nobody Fri Sep 25 17:45:46 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5D18B4AB1AD; Wed, 9 Sep 2026 22:05: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=1788991522; cv=none; b=eV6KhkWJuvpZ2HKWMprEKunuG2LeurkJduyB+MEA6QIgNbkNKFikJe7pQYW90wQfcG9qUBQK1gKEvJBg1ckBgNFGFB46VnUCPr1k6mreQj4aCabHJ1eOgGXssGzr2aG6hlEwZtSwc3mkzAOuLfQbmPH3BJtbHoD5SmConcrw7vo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788991522; c=relaxed/simple; bh=OR9+1hsuKbRKn30pFMBe4EEVo2eTrp7YqHpCpE24iZ4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=SLF3qzAHVUu9zmiCvwSR3pIv2JdR+yroqK2nwG7ANd789KZrCNk0/rCkgmKiJjmsxdvLZ1eReBTRrKxvZ8GiHzunaFXSUugKrT96/FnEjzW4GqwcWKs6iYHu/phV74Fd+7vF+WnaDTzlbyHlSbvfvDgOHEKYUHPJegQEGsC4mF4= 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=pRVpLrvB; 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="pRVpLrvB" 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 22DA2169C; Wed, 9 Sep 2026 15:05:16 -0700 (PDT) Received: from oss-apollo7005.lab.cambridge.arm.com (oss-apollo7005.lab.cambridge.arm.com [10.7.15.171]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A53D03F7D8; Wed, 9 Sep 2026 15:05:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788991519; bh=OR9+1hsuKbRKn30pFMBe4EEVo2eTrp7YqHpCpE24iZ4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pRVpLrvBP9BjA0mry/TfwCU2gz7cEV83i+RtpfdHpCUOG+n3qAl+DyS/ZlshdHf76 VHreVRsDoaG9ESb2yAfqYX4gAjAQ7lAwUc2RJm6gJr4jzTS7OcdsBWAW1/IhJSKt6J TCPja13n8+sdpTxcX9cs8BNDfCIuwo8qaEKq6YdQ= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, 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, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, philip.radford@arm.com Subject: [PATCH v9 13/13] powercap: arm_scmi: Synthetic zone enable/disable Date: Wed, 9 Sep 2026 22:04:37 +0000 Message-Id: <20260909220437.1254412-14-philip.radford@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260909220437.1254412-1-philip.radford@arm.com> References: <20260909220437.1254412-1-philip.radford@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" The synthetic instance root contols the same set of top-level SCMI domains already handled by the control-type enable/disable helpers previously introduced. Add synthetic zone enabled attribute to the existing per-instance helpers instead of duplicating the enable-state tracking and rollback logic. Signed-off-by: Philip Radford --- V8->V9 - Added mutex lock to instance_root_set_enable and instance_root_get_enable --- drivers/powercap/arm_scmi_powercap.c | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_sc= mi_powercap.c index aac0b00490b2..f42b1e8d228a 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -499,10 +499,41 @@ static int instance_root_get_constraint(struct powerc= ap_zone *pz, int cid, u64 * return -EOPNOTSUPP; } =20 +static int instance_root_set_enable(struct powercap_zone *pz, bool mode) +{ + struct scmi_powercap_root *pr =3D to_scmi_powercap_root(pz); + int ret; + + mutex_lock(&scmi_powercap_roots_lock); + ret =3D scmi_powercap_set_root_children_enable_state(pr, mode); + mutex_unlock(&scmi_powercap_roots_lock); + + return ret; +} + +static int instance_root_get_enable(struct powercap_zone *pz, bool *mode) +{ + struct scmi_powercap_root *pr =3D to_scmi_powercap_root(pz); + int ret; + + if (!mode) + return -EINVAL; + + mutex_lock(&scmi_powercap_roots_lock); + ret =3D scmi_powercap_read_root_children_enable_state(pr, mode); + if (!ret) + pr->enabled =3D *mode; + mutex_unlock(&scmi_powercap_roots_lock); + + return ret; +} + static const struct powercap_zone_ops instance_root_ops =3D { .get_max_power_range_uw =3D scmi_powercap_get_max_power_range_uw, .get_power_uw =3D instance_root_get_power_uw, .release =3D instance_root_release, + .set_enable =3D instance_root_set_enable, + .get_enable =3D instance_root_get_enable, }; =20 static const struct powercap_zone_constraint_ops instance_root_const_ops = =3D { --=20 2.25.1