* [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions
@ 2025-08-13 11:46 Cristian Marussi
2025-08-13 11:46 ` [PATCH 1/8] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers Cristian Marussi
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi
Hi all,
SCMIv4.0 [1] introduces some new features and commands into the Powercap
protocol. In a nutshell, such protocol changes add support for:
- setting multiple powercap limit/interval constraints for each SCMI
powercap domain which supports multiple Concurrent Power Limit
- enabling more Powercap commands to use Fastchannels mechanism
- adding multiple constraints support to the existing notifications
After a bit of needed updates in the SCMI core this series adds:
- support for the idea of optional multiple Concurrent Power Limit (CPLs)
- support for the new FCs
- support for extended notifications
- enable usage of such multiple constraint in the ARM SCMI Powercap driver
Note that the public SCMIv4.0 spec at [1] is currently still ALPHA0, so
this series could anyway need some minor rework along the way and
definitely will need to wait for a final public release before being
possibly merged.
Tested in a mocked setup implementing the new protocol extensions.
Based on v6.17-rc1.
Thanks,
Cristian
[1]: https://developer.arm.com/documentation/den0056/f/?lang=en
Cristian Marussi (8):
firmware: arm_scmi: Add an optional custom parameter to fastchannel
helpers
firmware: arm_scmi: Add bound iterators support
firmware: arm_scmi: Refactor powercap domain layout
firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support
firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support
include: trace: Add new parameter to trace_scmi_fc_call
powercap: arm_scmi: Enable multiple constraints support
drivers/firmware/arm_scmi/driver.c | 33 +-
drivers/firmware/arm_scmi/perf.c | 16 +-
drivers/firmware/arm_scmi/powercap.c | 843 ++++++++++++++++++++------
drivers/firmware/arm_scmi/protocols.h | 8 +-
drivers/powercap/arm_scmi_powercap.c | 54 +-
include/linux/scmi_protocol.h | 78 ++-
include/trace/events/scmi.h | 10 +-
7 files changed, 777 insertions(+), 265 deletions(-)
--
2.47.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/8] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-13 11:46 ` [PATCH 2/8] firmware: arm_scmi: Add bound iterators support Cristian Marussi
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, 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 <cristian.marussi@arm.com>
---
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 bd56a877fdfc..65bd545c0cb1 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1892,6 +1892,11 @@ static int scmi_iterator_run(void *iter)
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))
};
struct scmi_msg_resp_desc_fc {
@@ -1920,7 +1925,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;
@@ -1951,13 +1956,16 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
}
ret = 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;
info = t->tx.buf;
info->domain = cpu_to_le32(domain);
info->message_id = cpu_to_le32(message_id);
+ if (custom)
+ info->custom = cpu_to_le32(*custom);
/*
* 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/perf.c
index 683fd9b85c5c..99f2c5a510e0 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -847,25 +847,25 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
return;
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);
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);
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);
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_scmi/powercap.c
index 1fa79bba492e..3c7b77c9335d 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -717,24 +717,24 @@ static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
return;
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);
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);
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);
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);
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index d62c4469d1fd..8a96b78331f2 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -277,7 +277,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);
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/8] firmware: arm_scmi: Add bound iterators support
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
2025-08-13 11:46 ` [PATCH 1/8] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-13 11:46 ` [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout Cristian Marussi
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi
SCMI core stack provides some common helpers to handle in a unified way
multipart message replies: such iterator-helpers, when run, currently
process by default the whole set of available resources.
Introduce an alternative way to run the initialized iterator on a limited
range of resources.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/driver.c | 21 +++++++++++++++++++--
drivers/firmware/arm_scmi/protocols.h | 6 ++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 65bd545c0cb1..62f9cdd508c3 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1832,7 +1832,8 @@ static void *scmi_iterator_init(const struct scmi_protocol_handle *ph,
return i;
}
-static int scmi_iterator_run(void *iter)
+static int __scmi_iterator_run(void *iter, unsigned int *start,
+ unsigned int *end)
{
int ret = -EINVAL;
struct scmi_iterator_ops *iops;
@@ -1847,6 +1848,9 @@ static int scmi_iterator_run(void *iter)
ph = i->ph;
st = &i->state;
+ if (start)
+ st->desc_index = *start;
+
do {
iops->prepare_message(i->msg, st->desc_index, i->priv);
ret = ph->xops->do_xfer(ph, i->t);
@@ -1879,7 +1883,8 @@ static int scmi_iterator_run(void *iter)
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
*/
- } while (st->num_returned && st->num_remaining);
+ } while (st->num_returned && st->num_remaining &&
+ (!end || (st->desc_index <= min(*end, st->max_resources - 1))));
out:
/* Finalize and destroy iterator */
@@ -1889,6 +1894,17 @@ static int scmi_iterator_run(void *iter)
return ret;
}
+static int scmi_iterator_run(void *iter)
+{
+ return __scmi_iterator_run(iter, NULL, NULL);
+}
+
+static int scmi_iterator_run_bound(void *iter, unsigned int *start,
+ unsigned int *end)
+{
+ return __scmi_iterator_run(iter, start, end);
+}
+
struct scmi_msg_get_fc_info {
__le32 domain;
__le32 message_id;
@@ -2075,6 +2091,7 @@ static const struct scmi_proto_helpers_ops helpers_ops = {
.get_max_msg_size = scmi_common_get_max_msg_size,
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
+ .iter_response_run_bound = scmi_iterator_run_bound,
.protocol_msg_check = scmi_protocol_msg_check,
.fastchannel_init = scmi_common_fastchannel_init,
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index 8a96b78331f2..a48e212ac0e1 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -257,6 +257,10 @@ struct scmi_fc_info {
* provided in @ops.
* @iter_response_run: A common helper to trigger the run of a previously
* initialized iterator.
+ * @iter_response_run_bound: A common helper to trigger the run of a previously
+ * initialized iterator, but only within the
+ * specified, optional, @start and @end resource
+ * indexes.
* @protocol_msg_check: A common helper to check is a specific protocol message
* is supported.
* @fastchannel_init: A common helper used to initialize FC descriptors by
@@ -273,6 +277,8 @@ struct scmi_proto_helpers_ops {
unsigned int max_resources, u8 msg_id,
size_t tx_size, void *priv);
int (*iter_response_run)(void *iter);
+ int (*iter_response_run_bound)(void *iter,
+ unsigned int *start, unsigned int *end);
int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
u32 message_id, u32 *attributes);
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
2025-08-13 11:46 ` [PATCH 1/8] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers Cristian Marussi
2025-08-13 11:46 ` [PATCH 2/8] firmware: arm_scmi: Add bound iterators support Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-15 13:13 ` Dan Carpenter
2025-08-13 11:46 ` [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support Cristian Marussi
` (4 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, 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 <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 182 +++++++++++++++++----------
drivers/powercap/arm_scmi_powercap.c | 50 ++++----
include/linux/scmi_protocol.h | 74 +++++++----
3 files changed, 188 insertions(+), 118 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 3c7b77c9335d..3432f55ace14 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-2025 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
@@ -20,6 +20,8 @@
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+#define CPL0 0
+
enum scmi_powercap_protocol_cmd {
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
POWERCAP_CAP_GET = 0x4,
@@ -193,27 +195,26 @@ scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
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 = pinfo->powercaps + domain;
struct scmi_msg_resp_powercap_domain_attributes *resp;
ret = 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;
- put_unaligned_le32(domain, t->tx.buf);
+ put_unaligned_le32(dom_info->id, t->tx.buf);
resp = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
flags = le32_to_cpu(resp->attributes);
- dom_info->id = domain;
if (pinfo->notify_cap_cmd)
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
@@ -222,12 +223,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
dom_info->async_powercap_cap_set =
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
- dom_info->powercap_cap_config =
- SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+
dom_info->powercap_monitoring =
SUPPORTS_POWERCAP_MONITORING(flags);
- dom_info->powercap_pai_config =
- SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
dom_info->powercap_scale_mw =
SUPPORTS_POWER_UNITS_MW(flags);
dom_info->powercap_scale_uw =
@@ -237,13 +235,29 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
- dom_info->min_pai = le32_to_cpu(resp->min_pai);
- dom_info->max_pai = le32_to_cpu(resp->max_pai);
- dom_info->pai_step = le32_to_cpu(resp->pai_step);
- ret = scmi_powercap_validate(dom_info->min_pai,
- dom_info->max_pai,
- dom_info->pai_step,
- dom_info->powercap_pai_config);
+ dom_info->sustainable_power =
+ le32_to_cpu(resp->sustainable_power);
+ dom_info->accuracy = le32_to_cpu(resp->accuracy);
+
+ dom_info->parent_id = le32_to_cpu(resp->parent_id);
+ if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
+ (dom_info->parent_id >= pinfo->num_domains ||
+ dom_info->parent_id == dom_info->id)) {
+ dev_err(ph->dev,
+ "Platform reported inconsistent parent ID for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+ ret = -ENODEV;
+ }
+
+ dom_info->cpli[0].avg_ivl_config =
+ SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
+ dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_pai);
+ dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_pai);
+ dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->pai_step);
+ ret = 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",
@@ -251,13 +265,15 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
goto clean;
}
- dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
- dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
- dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
- ret = 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 =
+ SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+ dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
+ dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
+ dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
+ ret = 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",
@@ -265,19 +281,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
goto clean;
}
- dom_info->sustainable_power =
- le32_to_cpu(resp->sustainable_power);
- dom_info->accuracy = le32_to_cpu(resp->accuracy);
-
- dom_info->parent_id = le32_to_cpu(resp->parent_id);
- if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
- (dom_info->parent_id >= pinfo->num_domains ||
- dom_info->parent_id == dom_info->id)) {
- dev_err(ph->dev,
- "Platform reported inconsistent parent ID for domain %d - %s\n",
- dom_info->id, dom_info->name);
- ret = -ENODEV;
- }
+ /* Just using same short name */
+ strscpy(dom_info->cpli[0].name, dom_info->name,
+ SCMI_SHORT_NAME_MAX_SIZE);
}
clean:
@@ -289,12 +295,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);
return ret;
}
+static int
+scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
+ struct powercap_info *pinfo, u32 domain)
+{
+ struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
+
+ dom_info->num_cpli = 1;
+ dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
+ sizeof(*dom_info->cpli), GFP_KERNEL);
+ if (!dom_info->cpli)
+ return -ENOMEM;
+
+ dom_info->id = domain;
+ dom_info->cpli[0].id = 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 = ph->get_priv(ph);
@@ -336,10 +360,11 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
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 = 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 = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
dom->id, *power_cap, 0);
return 0;
@@ -349,7 +374,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
}
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;
@@ -360,12 +385,13 @@ static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
if (!dom)
return -EINVAL;
- return __scmi_powercap_cap_get(ph, dom, power_cap);
+ return __scmi_powercap_cap_get(ph, dom, cpl_id, power_cap);
}
static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
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;
@@ -407,21 +433,23 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
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 = -EINVAL;
const struct scmi_powercap_info *pc;
pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_cap_config)
+ if (!pc || !pc->cpli[cpl_id].cap_config)
return ret;
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;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
- struct scmi_fc_info *fci = &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 = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_CAP];
iowrite32(power_cap, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
@@ -429,7 +457,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
domain_id, power_cap, 0);
ret = 0;
} else {
- ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
+ ret = scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap,
ignore_dresp);
}
@@ -441,7 +469,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
}
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 = ph->get_priv(ph);
@@ -460,7 +488,7 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
return 0;
}
- return __scmi_powercap_cap_set(ph, pi, domain_id,
+ return __scmi_powercap_cap_set(ph, pi, domain_id, cpl_id,
power_cap, ignore_dresp);
}
@@ -486,7 +514,7 @@ static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
}
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 = ph->get_priv(ph);
@@ -495,8 +523,11 @@ static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
return -EINVAL;
dom = pi->powercaps + domain_id;
- if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
- *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
+ if (cpl_id > dom->num_cpli)
+ return -EINVAL;
+
+ if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr) {
+ *pai = 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;
@@ -505,6 +536,12 @@ static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
}
+static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *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 *ph,
u32 domain_id, u32 pai)
{
@@ -529,17 +566,18 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
}
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;
pc = 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 >= 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;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
- struct scmi_fc_info *fci = &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 = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI];
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
domain_id, pai, 0);
@@ -551,6 +589,12 @@ static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
}
+static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *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_handle *ph,
u32 domain_id, u32 *average_power,
u32 *pai)
@@ -646,11 +690,11 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
if (!pi->states[domain_id].last_pcap)
return -EINVAL;
- ret = __scmi_powercap_cap_set(ph, pi, domain_id,
+ ret = __scmi_powercap_cap_set(ph, pi, domain_id, CPL0,
pi->states[domain_id].last_pcap,
true);
} else {
- ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
+ ret = __scmi_powercap_cap_set(ph, pi, domain_id, CPL0, 0, true);
}
if (ret)
@@ -661,7 +705,7 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
* server could have ignored a disable request and kept enforcing some
* powercap limit requested by other agents.
*/
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
+ ret = scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap);
if (!ret)
pi->states[domain_id].enabled = !!power_cap;
@@ -683,7 +727,7 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
* Report always real platform state; platform could have ignored
* a previous disable request. Default true on any error.
*/
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
+ ret = scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap);
if (!ret)
*enable = !!power_cap;
@@ -700,8 +744,8 @@ static const struct scmi_powercap_proto_ops powercap_proto_ops = {
.cap_set = scmi_powercap_cap_set,
.cap_enable_set = scmi_powercap_cap_enable_set,
.cap_enable_get = scmi_powercap_cap_enable_get,
- .pai_get = scmi_powercap_pai_get,
- .pai_set = scmi_powercap_pai_set,
+ .avg_interval_get = scmi_powercap_avg_interval_get,
+ .avg_interval_set = scmi_powercap_avg_interval_set,
.measurements_get = scmi_powercap_measurements_get,
.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
@@ -997,18 +1041,18 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
* formed and correlated by sane parent-child relationship (if any).
*/
for (domain = 0; domain < pinfo->num_domains; domain++) {
- ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
+ ret = scmi_powercap_domain_initialize(ph, pinfo, domain);
if (ret)
return ret;
if (pinfo->powercaps[domain].fastchannels)
scmi_powercap_domain_init_fc(ph, domain,
- &pinfo->powercaps[domain].fc_info);
+ &pinfo->powercaps[domain].cpli[CPL0].fc_info);
/* Grab initial state when disable is supported. */
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
ret = __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_scmi_powercap.c
index a081f177e702..de2dc1f6f2a9 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -97,7 +97,7 @@ static const struct powercap_zone_ops zone_ops = {
};
static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz,
- u64 power_limit_uw, u32 *norm)
+ u64 power_limit_uw, int cid, u32 *norm)
{
bool scale_mw = spz->info->powercap_scale_mw;
u64 val;
@@ -108,9 +108,9 @@ static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz,
* the range [min_power_cap, max_power_cap] whose bounds are assured to
* be two unsigned 32bits quantities.
*/
- *norm = clamp_t(u32, val, spz->info->min_power_cap,
- spz->info->max_power_cap);
- *norm = rounddown(*norm, spz->info->power_cap_step);
+ *norm = clamp_t(u32, val, spz->info->cpli[cid].min_power_cap,
+ spz->info->cpli[cid].max_power_cap);
+ *norm = rounddown(*norm, spz->info->cpli[cid].power_cap_step);
val = (scale_mw) ? *norm * 1000 : *norm;
if (power_limit_uw != val)
@@ -125,12 +125,12 @@ static int scmi_powercap_set_power_limit_uw(struct powercap_zone *pz, int cid,
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
u32 norm_power;
- if (!spz->info->powercap_cap_config)
+ if (!spz->info->cpli[cid].cap_config)
return -EINVAL;
- scmi_powercap_normalize_cap(spz, power_uw, &norm_power);
+ scmi_powercap_normalize_cap(spz, power_uw, cid, &norm_power);
- 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, false);
}
static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
@@ -140,7 +140,7 @@ static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
u32 power;
int ret;
- ret = powercap_ops->cap_get(spz->ph, spz->info->id, &power);
+ ret = powercap_ops->cap_get(spz->ph, spz->info->id, cid, &power);
if (ret)
return ret;
@@ -152,19 +152,20 @@ static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
}
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 = clamp_t(u32, time_us, spz->info->min_pai, spz->info->max_pai);
- *norm = rounddown(*norm, spz->info->pai_step);
+ *norm = clamp_t(u32, time_us, spz->info->cpli[cid].min_avg_ivl,
+ spz->info->cpli[cid].max_avg_ivl);
+ *norm = rounddown(*norm, spz->info->cpli[cid].avg_ivl_step);
if (time_us != *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);
}
@@ -174,12 +175,13 @@ static int scmi_powercap_set_time_window_us(struct powercap_zone *pz, int cid,
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
u32 norm_pai;
- if (!spz->info->powercap_pai_config)
+ if (!spz->info->cpli[cid].avg_ivl_config)
return -EINVAL;
- scmi_powercap_normalize_time(spz, time_window_us, &norm_pai);
+ scmi_powercap_normalize_time(spz, time_window_us, cid, &norm_pai);
- 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);
}
static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int cid,
@@ -189,7 +191,7 @@ static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int cid,
int ret;
u32 pai;
- ret = powercap_ops->pai_get(spz->ph, spz->info->id, &pai);
+ ret = powercap_ops->avg_interval_get(spz->ph, spz->info->id, cid, &pai);
if (ret)
return ret;
@@ -203,7 +205,7 @@ static int scmi_powercap_get_max_power_uw(struct powercap_zone *pz, int cid,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *max_power_uw = spz->info->max_power_cap;
+ *max_power_uw = spz->info->cpli[cid].max_power_cap;
if (spz->info->powercap_scale_mw)
*max_power_uw *= 1000;
@@ -215,7 +217,7 @@ static int scmi_powercap_get_min_power_uw(struct powercap_zone *pz, int cid,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *min_power_uw = spz->info->min_power_cap;
+ *min_power_uw = spz->info->cpli[cid].min_power_cap;
if (spz->info->powercap_scale_mw)
*min_power_uw *= 1000;
@@ -227,7 +229,7 @@ static int scmi_powercap_get_max_time_window_us(struct powercap_zone *pz,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *time_window_us = spz->info->max_pai;
+ *time_window_us = spz->info->cpli[cid].max_avg_ivl;
return 0;
}
@@ -237,14 +239,16 @@ static int scmi_powercap_get_min_time_window_us(struct powercap_zone *pz,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *time_window_us = (u64)spz->info->min_pai;
+ *time_window_us = (u64)spz->info->cpli[cid].min_avg_ivl;
return 0;
}
static const char *scmi_powercap_get_name(struct powercap_zone *pz, int cid)
{
- return "SCMI power-cap";
+ struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
+
+ return spz->info->cpli[cid].name;
}
static const struct powercap_zone_constraint_ops constraint_ops = {
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 688466a0e816..0f48d8dcf561 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-2025 ARM Ltd.
*/
#ifndef _LINUX_SCMI_PROTOCOL_H
@@ -609,6 +609,35 @@ struct scmi_voltage_proto_ops {
s32 *volt_uV);
};
+/**
+ * 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 CPL
+ */
+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
*
@@ -617,21 +646,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 domain
* under normal conditions.
* @accuracy: The accuracy with which the power is measured and reported in
@@ -639,30 +662,25 @@ struct scmi_voltage_proto_ops {
* @parent_id: Identifier of the containing parent power capping domain, or 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;
};
/**
@@ -691,8 +709,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 domain.
- * @pai_get: get the current PAI value for the specified domain.
- * @pai_set: set the PAI value for the specified domain to the provided value.
+ * @avg_interval_get: get the current averaging interval value for the specified
+ * 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 specified
+ * 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.
@@ -716,17 +738,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,
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
` (2 preceding siblings ...)
2025-08-13 11:46 ` [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-14 13:54 ` kernel test robot
2025-08-13 11:46 ` [PATCH 5/8] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support Cristian Marussi
` (3 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi
Add SCMIv4.0 Powercap support for enumerating multiple CPLs of a domain
when available.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 477 +++++++++++++++++++++------
include/linux/scmi_protocol.h | 1 +
2 files changed, 379 insertions(+), 99 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 3432f55ace14..37fe3bd0c099 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 = 0xa,
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
+ POWERCAP_CPC_ATTRIBUTES = 0xd,
};
enum {
@@ -69,17 +70,56 @@ struct scmi_msg_resp_powercap_domain_attributes {
__le32 parent_id;
};
+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;
};
+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;
};
struct scmi_msg_resp_powercap_meas_get {
@@ -112,6 +152,33 @@ struct scmi_powercap_meas_changed_notify_payld {
__le32 power;
};
+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;
@@ -130,6 +197,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,
+ u32 domain_id, 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);
};
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
@@ -193,111 +265,241 @@ scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
return 0;
}
+static void iter_powercap_cpls_prepare_message(void *message,
+ unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_powercap_cpc *msg = message;
+ const struct scmi_cpls_priv *p = priv;
+
+ msg->domain_id = cpu_to_le32(p->domain_id);
+ msg->desc_index = 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 = response;
+
+ st->num_returned = NUM_RETURNED(r->num_cpl);
+ st->num_remaining = NUM_REMAINING(r->num_cpl);
+
+ 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)
+iter_powercap_cpls_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
{
- int ret;
- u32 flags;
- struct scmi_xfer *t;
- struct scmi_msg_resp_powercap_domain_attributes *resp;
+ const struct scmi_msg_resp_powercap_cpc *r = response;
+ struct scmi_cpls_priv *p = priv;
+ struct scmi_powercap_cpl_info *cpl;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
- sizeof(dom_info->id), sizeof(*resp), &t);
- if (ret)
- return ret;
+ cpl = &p->cpli[st->desc_index + st->loop_idx];
- put_unaligned_le32(dom_info->id, t->tx.buf);
- resp = t->rx.buf;
+ cpl->id = le32_to_cpu(r->desc[st->loop_idx].cpli);
+ cpl->cap_config = r->desc[st->loop_idx].flags & BIT(0);
- ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
- flags = le32_to_cpu(resp->attributes);
+ cpl->min_power_cap = le32_to_cpu(r->desc[st->loop_idx].min_power_cap);
+ cpl->max_power_cap = le32_to_cpu(r->desc[st->loop_idx].max_power_cap);
+ cpl->power_cap_step = le32_to_cpu(r->desc[st->loop_idx].power_cap_step);
+ if (!cpl->power_cap_step && cpl->min_power_cap != cpl->max_power_cap)
+ return -EINVAL;
+
+ cpl->min_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].min_cai);
+ cpl->max_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].max_cai);
+ cpl->avg_ivl_step = le32_to_cpu(r->desc[st->loop_idx].cai_step);
+ if (!cpl->avg_ivl_step && cpl->min_avg_ivl != cpl->max_avg_ivl)
+ return -EINVAL;
- if (pinfo->notify_cap_cmd)
+ cpl->avg_ivl_config = cpl->min_avg_ivl != 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 = {
+ .prepare_message = iter_powercap_cpls_prepare_message,
+ .update_state = iter_powercap_cpls_update_state,
+ .process_response = iter_powercap_cpls_process_response,
+ };
+ struct scmi_cpls_priv cpriv = {
+ .domain_id = dom_info->id,
+ .cpli = dom_info->cpli,
+ };
+
+ iter = 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 = r;
+ u32 flags = resp->attributes;
+ bool cap_config;
+ int ret;
+
+ cap_config = SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+ if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3) {
+ dom_info->num_cpli = 1;
+ } else {
+ dom_info->num_cpli = le32_get_bits(resp->attributes,
+ GENMASK(18, 15));
+ if (cap_config && !dom_info->num_cpli)
+ return -EINVAL;
+ }
+
+ dom_info->cpli = 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(pinfo->version) < 0x3)
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
- if (pinfo->notify_measurements_cmd)
- dom_info->notify_powercap_measurement_change =
- SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
- dom_info->async_powercap_cap_set =
- SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
-
- dom_info->powercap_monitoring =
- SUPPORTS_POWERCAP_MONITORING(flags);
- dom_info->powercap_scale_mw =
- SUPPORTS_POWER_UNITS_MW(flags);
- dom_info->powercap_scale_uw =
- SUPPORTS_POWER_UNITS_UW(flags);
- dom_info->fastchannels =
- SUPPORTS_POWERCAP_FASTCHANNELS(flags);
-
- strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
-
- dom_info->sustainable_power =
- le32_to_cpu(resp->sustainable_power);
- dom_info->accuracy = le32_to_cpu(resp->accuracy);
-
- dom_info->parent_id = le32_to_cpu(resp->parent_id);
- if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
- (dom_info->parent_id >= pinfo->num_domains ||
- dom_info->parent_id == dom_info->id)) {
- dev_err(ph->dev,
- "Platform reported inconsistent parent ID for domain %d - %s\n",
- dom_info->id, dom_info->name);
- ret = -ENODEV;
- }
+ else
+ dom_info->notify_powercap_cap_change =
+ SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(flags);
+ }
+
+ if (pinfo->notify_measurements_cmd)
+ dom_info->notify_powercap_measurement_change =
+ SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+
+ dom_info->extended_names = SUPPORTS_EXTENDED_NAMES(flags);
+
+ dom_info->async_powercap_cap_set =
+ SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
+
+ dom_info->powercap_monitoring =
+ SUPPORTS_POWERCAP_MONITORING(flags);
+ dom_info->powercap_scale_mw =
+ SUPPORTS_POWER_UNITS_MW(flags);
+ dom_info->powercap_scale_uw =
+ SUPPORTS_POWER_UNITS_UW(flags);
+ dom_info->fastchannels =
+ SUPPORTS_POWERCAP_FASTCHANNELS(flags);
+
+ strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ dom_info->sustainable_power =
+ le32_to_cpu(resp->sustainable_power);
+ dom_info->accuracy = le32_to_cpu(resp->accuracy);
+
+ dom_info->parent_id = le32_to_cpu(resp->parent_id);
+ if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
+ (dom_info->parent_id >= pinfo->num_domains ||
+ dom_info->parent_id == 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 = CPL0;
+ if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3)
dom_info->cpli[0].avg_ivl_config =
SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
+ else
+ dom_info->cpli[0].avg_ivl_config =
+ SUPPORTS_POWERCAP_CAI_CONFIGURATION(flags);
+
+ if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3) {
dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_pai);
dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_pai);
dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->pai_step);
- ret = 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;
- }
+ } else {
+ struct scmi_msg_resp_powercap_domain_attributes_v3 *resp = r;
- dom_info->cpli[0].cap_config =
- SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
- dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
- dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
- dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
- ret = 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;
- }
+ dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_cai);
+ dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_cai);
+ dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->cai_step);
+ }
- /* Just using same short name */
- strscpy(dom_info->cpli[0].name, dom_info->name,
- SCMI_SHORT_NAME_MAX_SIZE);
+ ret = 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;
}
-clean:
+ dom_info->cpli[0].cap_config = cap_config;
+ dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
+ dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
+ dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
+ ret = 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;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_powercap_domain_attributes *resp;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
+ sizeof(dom_info->id), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(dom_info->id, t->tx.buf);
+ resp = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ ret = scmi_powercap_domain_attrs_process(ph, pinfo, dom_info, resp);
+
ph->xops->xfer_put(ph, t);
/*
* 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);
+ /* When protocol version > 0x3 there can possibly be more than 1 CPLs */
+ if (!ret && dom_info->num_cpli > 1)
+ ret = scmi_powercap_cpls_enumerate(ph, dom_info);
+
return ret;
}
@@ -307,14 +509,7 @@ scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
{
struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
- dom_info->num_cpli = 1;
- dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
- sizeof(*dom_info->cpli), GFP_KERNEL);
- if (!dom_info->cpli)
- return -ENOMEM;
-
dom_info->id = domain;
- dom_info->cpli[0].id = CPL0;
return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
}
@@ -338,7 +533,7 @@ scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
}
static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_cap)
+ u32 domain_id, u32 cpl_id, u32 *power_cap)
{
int ret;
struct scmi_xfer *t;
@@ -349,6 +544,33 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
return ret;
put_unaligned_le32(domain_id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *power_cap = 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,
+ u32 domain_id, u32 cpl_id,
+ u32 *power_cap)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_get_v3 *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg),
+ sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->cpli = cpu_to_le32(cpl_id);
+
ret = ph->xops->do_xfer(ph, t);
if (!ret)
*power_cap = get_unaligned_le32(t->rx.buf);
@@ -362,6 +584,8 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_info *dom,
u32 cpl_id, u32 *power_cap)
{
+ struct powercap_info *pi = ph->get_priv(ph);
+
if (dom->cpli[cpl_id].fc_info &&
dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) {
*power_cap = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
@@ -370,7 +594,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
return 0;
}
- return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
+ return pi->xfer_cap_get(ph, dom->id, cpl_id, power_cap);
}
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
@@ -403,7 +627,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
return ret;
msg = t->tx.buf;
- msg->domain = cpu_to_le32(pc->id);
+ msg->domain_id = cpu_to_le32(pc->id);
msg->flags =
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
@@ -417,7 +641,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
struct scmi_msg_resp_powercap_cap_set_complete *resp;
resp = t->rx.buf;
- if (le32_to_cpu(resp->domain) == pc->id)
+ if (le32_to_cpu(resp->domain_id) == pc->id)
dev_dbg(ph->dev,
"Powercap ID %d CAP set async to %u\n",
pc->id,
@@ -431,6 +655,51 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
return ret;
}
+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 = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
+ sizeof(*msg), 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(pc->id);
+ msg->cpli = cpu_to_le32(cpl_id);
+ msg->flags =
+ cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
+ FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
+ msg->power_cap = cpu_to_le32(power_cap);
+
+ if (!pc->async_powercap_cap_set || ignore_dresp) {
+ ret = ph->xops->do_xfer(ph, t);
+ } else {
+ ret = ph->xops->do_xfer_with_response(ph, t);
+ if (!ret) {
+ struct scmi_msg_resp_powercap_cap_set_complete_v3 *resp;
+
+ resp = t->rx.buf;
+ if (le32_to_cpu(resp->domain_id) == pc->id &&
+ le32_to_cpu(resp->cpli) == 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 = -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)
@@ -457,12 +726,12 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
domain_id, power_cap, 0);
ret = 0;
} else {
- ret = scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap,
- ignore_dresp);
+ ret = pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp);
}
- /* Save the last explicitly set non-zero powercap value */
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
+ /* Save the last explicitly set non-zero powercap value for CPL0 */
+ if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret &&
+ cpl_id == CPL0 && power_cap)
pi->states[domain_id].last_pcap = power_cap;
return ret;
@@ -481,8 +750,8 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
if (!power_cap)
return -EINVAL;
- /* Just log the last set request if acting on a disabled domain */
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
+ /* Just log the last set request on CPL0 on a disabled domain */
+ if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && cpl_id == CPL0 &&
!pi->states[domain_id].enabled) {
pi->states[domain_id].last_pcap = power_cap;
return 0;
@@ -555,7 +824,7 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
return ret;
msg = t->tx.buf;
- msg->domain = cpu_to_le32(domain_id);
+ msg->domain_id = cpu_to_le32(domain_id);
msg->flags = cpu_to_le32(0);
msg->value = cpu_to_le32(pai);
@@ -1019,6 +1288,17 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo)
return -ENOMEM;
+ pinfo->version = version;
+ ph->set_priv(ph, pinfo, version);
+
+ if (PROTOCOL_REV_MAJOR(version) < 0x3) {
+ pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get;
+ pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set;
+ } else {
+ pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get_v3;
+ pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set_v3;
+ }
+
ret = scmi_powercap_attributes_get(ph, pinfo);
if (ret)
return ret;
@@ -1062,8 +1342,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
}
}
- pinfo->version = version;
- return ph->set_priv(ph, pinfo, version);
+ return 0;
}
static const struct scmi_protocol scmi_powercap = {
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 0f48d8dcf561..a98213bff60a 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -673,6 +673,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;
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/8] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
` (3 preceding siblings ...)
2025-08-13 11:46 ` [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-13 11:46 ` [PATCH 6/8] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support Cristian Marussi
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi
Add support for new SCMIv4.0 Powercap Fastchannels.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 327 ++++++++++++++++++---------
1 file changed, 224 insertions(+), 103 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 37fe3bd0c099..1c4b79249c40 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 = 0x4,
POWERCAP_CAP_SET = 0x5,
POWERCAP_PAI_GET = 0x6,
+ POWERCAP_MAI_GET = POWERCAP_PAI_GET,
POWERCAP_PAI_SET = 0x7,
+ POWERCAP_MAI_SET = POWERCAP_PAI_SET,
POWERCAP_DOMAIN_NAME_GET = 0x8,
POWERCAP_MEASUREMENTS_GET = 0x9,
POWERCAP_CAP_NOTIFY = 0xa,
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
POWERCAP_CPC_ATTRIBUTES = 0xd,
+ POWERCAP_CAI_GET = 0xe,
+ POWERCAP_CAI_SET = 0xf,
};
enum {
POWERCAP_FC_CAP,
- POWERCAP_FC_PAI,
- POWERCAP_FC_MAX,
+ POWERCAP_FC_XAI,
+ POWERCAP_FC_MAI,
+ POWERCAP_FC_MEASUREMENT,
+ POWERCAP_FC_MAX
};
struct scmi_msg_resp_powercap_domain_attributes {
@@ -91,12 +97,12 @@ struct scmi_msg_resp_powercap_domain_attributes_v3 {
__le32 cai_step;
};
-struct scmi_msg_powercap_get_v3 {
+struct scmi_msg_powercap_cap_or_cai_get_v3 {
__le32 domain_id;
__le32 cpli;
};
-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;
};
-struct scmi_msg_powercap_set_cap_v3 {
+struct scmi_msg_powercap_cap_set_v3 {
__le32 domain_id;
__le32 cpli;
__le32 flags;
__le32 power_cap;
};
+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;
@@ -202,6 +215,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);
};
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
@@ -503,17 +520,6 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
return ret;
}
-static int
-scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
- struct powercap_info *pinfo, u32 domain)
-{
- struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
-
- dom_info->id = 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 = ph->get_priv(ph);
@@ -560,7 +566,7 @@ static int scmi_powercap_xfer_cap_get_v3(const struct scmi_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;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg),
sizeof(u32), &t);
@@ -619,7 +625,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;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
sizeof(*msg), 0, &t);
@@ -662,7 +668,7 @@ static int scmi_powercap_xfer_cap_set_v3(const struct scmi_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;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
sizeof(*msg), 0, &t);
@@ -761,8 +767,9 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
power_cap, ignore_dresp);
}
-static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
- 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;
@@ -775,58 +782,105 @@ static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
put_unaligned_le32(domain_id, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
if (!ret)
- *pai = get_unaligned_le32(t->rx.buf);
+ *ivl = get_unaligned_le32(t->rx.buf);
ph->xops->xfer_put(ph, t);
return ret;
}
-static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 cpl_id, u32 *pai)
+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 = ph->xops->xfer_get_init(ph, POWERCAP_CAI_GET, sizeof(*msg),
+ sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->cpli = cpu_to_le32(cpl_id);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *ivl = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *val)
{
struct scmi_powercap_info *dom;
struct powercap_info *pi = ph->get_priv(ph);
- if (!pai || domain_id >= pi->num_domains)
+ if (!val || domain_id >= pi->num_domains)
return -EINVAL;
dom = pi->powercaps + domain_id;
- if (cpl_id > dom->num_cpli)
+ if (cpl_id >= dom->num_cpli)
return -EINVAL;
- if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr) {
- *pai = 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 = (PROTOCOL_REV_MAJOR(pi->version) < 0x3) ?
+ POWERCAP_PAI_GET : POWERCAP_CAI_GET;
+
+ *val = 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;
}
- return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
+ return pi->xfer_avg_ivl_get(ph, domain_id, cpl_id, val);
}
-static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *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 = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, sizeof(*msg), 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->flags = cpu_to_le32(0);
+ msg->value = cpu_to_le32(ivl);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
}
-static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
- 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;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
- sizeof(*msg), 0, &t);
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAI_SET, sizeof(*msg), 0, &t);
if (ret)
return ret;
msg = t->tx.buf;
msg->domain_id = cpu_to_le32(domain_id);
msg->flags = cpu_to_le32(0);
- msg->value = cpu_to_le32(pai);
+ msg->cai = cpu_to_le32(ivl);
+ msg->cpli = cpu_to_le32(cpl_id);
ret = ph->xops->do_xfer(ph, t);
@@ -834,48 +888,42 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
return ret;
}
-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_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 ivl)
{
const struct scmi_powercap_info *pc;
+ struct powercap_info *pi = ph->get_priv(ph);
pc = scmi_powercap_dom_info_get(ph, domain_id);
if (!pc || cpl_id >= 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;
- if (pc->cpli[cpl_id].fc_info && pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].set_addr) {
- struct scmi_fc_info *fci = &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 = (PROTOCOL_REV_MAJOR(pi->version) < 0x3) ?
+ POWERCAP_PAI_SET : POWERCAP_CAI_SET;
+ struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI];
- 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;
}
- return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
+ return pi->xfer_avg_ivl_set(ph, domain_id, cpl_id, ivl);
}
-static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *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_handle *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 = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_monitoring || !pai || !average_power)
- return -EINVAL;
ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
sizeof(u32), sizeof(*resp), &t);
@@ -883,17 +931,42 @@ static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
return ret;
resp = t->rx.buf;
- put_unaligned_le32(domain_id, t->tx.buf);
+ put_unaligned_le32(pc->id, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
- *average_power = le32_to_cpu(resp->power);
- *pai = le32_to_cpu(resp->pai);
+ *avg_power = le32_to_cpu(resp->power);
+ *avg_ivl = le32_to_cpu(resp->pai);
}
ph->xops->xfer_put(ph, t);
return ret;
}
+static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 *avg_power,
+ u32 *avg_ivl)
+{
+ const struct scmi_powercap_info *pc;
+ struct scmi_fc_info *fci;
+
+ pc = 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 = pc->cpli[CPL0].fc_info;
+ if (fci && fci[POWERCAP_FC_MEASUREMENT].get_addr) {
+ *avg_power = ioread32(fci[POWERCAP_FC_MEASUREMENT].get_addr);
+ /* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */
+ *avg_ivl = 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,
@@ -1021,37 +1094,79 @@ static const struct scmi_powercap_proto_ops powercap_proto_ops = {
};
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 = 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 = fc;
+ struct powercap_info *pi = ph->get_priv(ph);
+
+ for (int id = 0; id < dom_info->num_cpli; id++) {
+ struct scmi_fc_info *fc;
+ u32 *cpl_id, zero_cpl_id = 0;
+
+ fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
+ if (!fc)
+ return;
+
+ /* NOTE THAT when num_cpli == 1 the arg *cpl_id is 0 */
+ cpl_id = (PROTOCOL_REV_MAJOR(pi->version) >= 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(pi->version) < 0x3) {
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_PAI_SET, 4,
+ dom_info->id, NULL,
+ &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_PAI_GET, 4,
+ dom_info->id, NULL,
+ &fc[POWERCAP_FC_XAI].get_addr, NULL,
+ &fc[POWERCAP_FC_XAI].rate_limit);
+ } else {
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_CAI_SET, 4,
+ dom_info->id, &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, &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 = fc;
+ }
}
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
@@ -1294,9 +1409,14 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
if (PROTOCOL_REV_MAJOR(version) < 0x3) {
pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get;
pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set;
+ pinfo->xfer_avg_ivl_get = scmi_powercap_xfer_avg_interval_get;
+ pinfo->xfer_avg_ivl_set = scmi_powercap_xfer_avg_interval_set;
+
} else {
pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get_v3;
pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set_v3;
+ pinfo->xfer_avg_ivl_get = scmi_powercap_xfer_avg_interval_get_v3;
+ pinfo->xfer_avg_ivl_set = scmi_powercap_xfer_avg_interval_set_v3;
}
ret = scmi_powercap_attributes_get(ph, pinfo);
@@ -1321,18 +1441,19 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
* formed and correlated by sane parent-child relationship (if any).
*/
for (domain = 0; domain < pinfo->num_domains; domain++) {
- ret = scmi_powercap_domain_initialize(ph, pinfo, domain);
+ struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
+
+ dom_info->id = domain;
+ ret = scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
if (ret)
return ret;
- 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);
/* Grab initial state when disable is supported. */
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
- ret = __scmi_powercap_cap_get(ph,
- &pinfo->powercaps[domain], CPL0,
+ ret = __scmi_powercap_cap_get(ph, dom_info, CPL0,
&pinfo->states[domain].last_pcap);
if (ret)
return ret;
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/8] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
` (4 preceding siblings ...)
2025-08-13 11:46 ` [PATCH 5/8] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-13 11:46 ` [PATCH 7/8] include: trace: Add new parameter to trace_scmi_fc_call Cristian Marussi
2025-08-13 11:46 ` [PATCH 8/8] powercap: arm_scmi: Enable multiple constraints support Cristian Marussi
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, 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 <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 13 +++++++++----
include/linux/scmi_protocol.h | 3 ++-
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 1c4b79249c40..350dd1f135ca 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -18,7 +18,7 @@
#include "notify.h"
/* Updated only after ALL the mandatory features for that version are merged */
-#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
#define CPL0 0
@@ -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;
};
struct scmi_powercap_meas_changed_notify_payld {
@@ -1308,14 +1309,18 @@ scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_cap_changed_notify_payld *p = payld;
struct scmi_powercap_cap_changed_report *r = report;
- if (sizeof(*p) != payld_sz)
+ if (sizeof(*p) > payld_sz)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->power_cap = le32_to_cpu(p->power_cap);
- r->pai = le32_to_cpu(p->pai);
+ r->avg_ivl = le32_to_cpu(p->avg_ivl);
+ if (sizeof(*p) == payld_sz)
+ r->cpli = le32_to_cpu(p->cpli);
+ else
+ r->cpli = 0;
*src_id = r->domain_id;
rep = r;
break;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index a98213bff60a..303d9183e1da 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -1125,7 +1125,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;
};
struct scmi_powercap_meas_changed_report {
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 7/8] include: trace: Add new parameter to trace_scmi_fc_call
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
` (5 preceding siblings ...)
2025-08-13 11:46 ` [PATCH 6/8] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
2025-08-13 11:46 ` [PATCH 8/8] powercap: arm_scmi: Enable multiple constraints support Cristian Marussi
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, 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 <cristian.marussi@arm.com>
---
drivers/firmware/arm_scmi/perf.c | 8 ++++----
drivers/firmware/arm_scmi/powercap.c | 12 +++++++-----
include/trace/events/scmi.h | 10 ++++++----
3 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 99f2c5a510e0..3b7dca4854e4 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -563,7 +563,7 @@ static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT];
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);
@@ -648,7 +648,7 @@ static int __scmi_perf_limits_get(const struct scmi_protocol_handle *ph,
*max_perf = ioread32(fci->get_addr);
*min_perf = 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;
}
@@ -718,7 +718,7 @@ static int __scmi_perf_level_set(const struct scmi_protocol_handle *ph,
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LEVEL];
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;
@@ -781,7 +781,7 @@ static int __scmi_perf_level_get(const struct scmi_protocol_handle *ph,
if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) {
*level = 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;
}
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 350dd1f135ca..a8b442e9b818 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -597,7 +597,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) {
*power_cap = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
- dom->id, *power_cap, 0);
+ dom->id, cpl_id, *power_cap, 0);
return 0;
}
@@ -730,7 +730,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_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 = 0;
} else {
ret = pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp);
@@ -835,7 +835,8 @@ static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
POWERCAP_PAI_GET : POWERCAP_CAI_GET;
*val = 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;
}
@@ -908,7 +909,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 = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI];
- 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;
@@ -961,7 +963,7 @@ static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
/* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */
*avg_ivl = 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;
}
diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h
index 703b7bb68e44..8be1e3837386 100644
--- a/include/trace/events/scmi.h
+++ b/include/trace/events/scmi.h
@@ -10,13 +10,14 @@
#define TRACE_SCMI_MAX_TAG_LEN 6
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),
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 = protocol_id;
__entry->msg_id = msg_id;
__entry->res_id = res_id;
+ __entry->sub_id = sub_id;
__entry->val1 = val1;
__entry->val2 = val2;
),
- TP_printk("pt=%02X msg_id=%02X res_id:%u vals=%u:%u",
+ TP_printk("pt=%02X msg_id=%02X res_id:%u sub_id:%u vals=%u:%u",
__entry->protocol_id, __entry->msg_id,
- __entry->res_id, __entry->val1, __entry->val2)
+ __entry->res_id, __entry->sub_id, __entry->val1, __entry->val2)
);
TRACE_EVENT(scmi_xfer_begin,
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 8/8] powercap: arm_scmi: Enable multiple constraints support
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
` (6 preceding siblings ...)
2025-08-13 11:46 ` [PATCH 7/8] include: trace: Add new parameter to trace_scmi_fc_call Cristian Marussi
@ 2025-08-13 11:46 ` Cristian Marussi
7 siblings, 0 replies; 11+ messages in thread
From: Cristian Marussi @ 2025-08-13 11:46 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Rafael J. Wysocki
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" <rafael@kernel.org>
CC: linux-pm@vger.kernel.org
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
drivers/powercap/arm_scmi_powercap.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index de2dc1f6f2a9..559767e143b0 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-2025 ARM Ltd.
*/
#include <linux/device.h>
@@ -311,7 +311,7 @@ static int scmi_powercap_register_zone(struct scmi_powercap_root *pr,
z = 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 = scmi_powercap_get_zone_height(spz);
spz->registered = true;
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
2025-08-13 11:46 ` [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support Cristian Marussi
@ 2025-08-14 13:54 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-08-14 13:54 UTC (permalink / raw)
To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
linux-pm
Cc: oe-kbuild-all, sudeep.holla, james.quinlan, f.fainelli,
vincent.guittot, etienne.carriere, peng.fan, michal.simek,
quic_sibis, dan.carpenter, d-gole, souvik.chakravarty,
Cristian Marussi
Hi Cristian,
kernel test robot noticed the following build warnings:
[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/bleeding-edge soc/for-next trace/for-next linus/master v6.17-rc1 next-20250814]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Cristian-Marussi/firmware-arm_scmi-Add-an-optional-custom-parameter-to-fastchannel-helpers/20250813-195150
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link: https://lore.kernel.org/r/20250813114609.1305571-5-cristian.marussi%40arm.com
patch subject: [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
config: arm64-randconfig-r111-20250814 (https://download.01.org/0day-ci/archive/20250814/202508142109.z6QIwr0w-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 14.3.0
reproduce: (https://download.01.org/0day-ci/archive/20250814/202508142109.z6QIwr0w-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508142109.z6QIwr0w-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/firmware/arm_scmi/powercap.c:302:48: sparse: sparse: restricted __le32 degrades to integer
>> drivers/firmware/arm_scmi/powercap.c:353:25: sparse: sparse: incorrect type in initializer (different base types) @@ expected unsigned int [usertype] flags @@ got restricted __le32 [usertype] attributes @@
drivers/firmware/arm_scmi/powercap.c:353:25: sparse: expected unsigned int [usertype] flags
drivers/firmware/arm_scmi/powercap.c:353:25: sparse: got restricted __le32 [usertype] attributes
vim +302 drivers/firmware/arm_scmi/powercap.c
289
290 static int
291 iter_powercap_cpls_process_response(const struct scmi_protocol_handle *ph,
292 const void *response,
293 struct scmi_iterator_state *st, void *priv)
294 {
295 const struct scmi_msg_resp_powercap_cpc *r = response;
296 struct scmi_cpls_priv *p = priv;
297 struct scmi_powercap_cpl_info *cpl;
298
299 cpl = &p->cpli[st->desc_index + st->loop_idx];
300
301 cpl->id = le32_to_cpu(r->desc[st->loop_idx].cpli);
> 302 cpl->cap_config = r->desc[st->loop_idx].flags & BIT(0);
303
304 cpl->min_power_cap = le32_to_cpu(r->desc[st->loop_idx].min_power_cap);
305 cpl->max_power_cap = le32_to_cpu(r->desc[st->loop_idx].max_power_cap);
306 cpl->power_cap_step = le32_to_cpu(r->desc[st->loop_idx].power_cap_step);
307 if (!cpl->power_cap_step && cpl->min_power_cap != cpl->max_power_cap)
308 return -EINVAL;
309
310 cpl->min_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].min_cai);
311 cpl->max_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].max_cai);
312 cpl->avg_ivl_step = le32_to_cpu(r->desc[st->loop_idx].cai_step);
313 if (!cpl->avg_ivl_step && cpl->min_avg_ivl != cpl->max_avg_ivl)
314 return -EINVAL;
315
316 cpl->avg_ivl_config = cpl->min_avg_ivl != cpl->max_avg_ivl;
317
318 strscpy(cpl->name, r->desc[st->loop_idx].name, SCMI_SHORT_NAME_MAX_SIZE);
319
320 return 0;
321 }
322
323 static int scmi_powercap_cpls_enumerate(const struct scmi_protocol_handle *ph,
324 struct scmi_powercap_info *dom_info)
325 {
326 void *iter;
327 struct scmi_iterator_ops ops = {
328 .prepare_message = iter_powercap_cpls_prepare_message,
329 .update_state = iter_powercap_cpls_update_state,
330 .process_response = iter_powercap_cpls_process_response,
331 };
332 struct scmi_cpls_priv cpriv = {
333 .domain_id = dom_info->id,
334 .cpli = dom_info->cpli,
335 };
336
337 iter = ph->hops->iter_response_init(ph, &ops, dom_info->num_cpli,
338 POWERCAP_CPC_ATTRIBUTES,
339 sizeof(struct scmi_msg_powercap_cpc),
340 &cpriv);
341 if (IS_ERR(iter))
342 return PTR_ERR(iter);
343
344 return ph->hops->iter_response_run(iter);
345 }
346
347 static int
348 scmi_powercap_domain_attrs_process(const struct scmi_protocol_handle *ph,
349 struct powercap_info *pinfo,
350 struct scmi_powercap_info *dom_info, void *r)
351 {
352 struct scmi_msg_resp_powercap_domain_attributes *resp = r;
> 353 u32 flags = resp->attributes;
354 bool cap_config;
355 int ret;
356
357 cap_config = SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
358 if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3) {
359 dom_info->num_cpli = 1;
360 } else {
361 dom_info->num_cpli = le32_get_bits(resp->attributes,
362 GENMASK(18, 15));
363 if (cap_config && !dom_info->num_cpli)
364 return -EINVAL;
365 }
366
367 dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
368 sizeof(*dom_info->cpli), GFP_KERNEL);
369 if (!dom_info->cpli)
370 return -ENOMEM;
371
372 if (pinfo->notify_cap_cmd) {
373 if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3)
374 dom_info->notify_powercap_cap_change =
375 SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
376 else
377 dom_info->notify_powercap_cap_change =
378 SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(flags);
379 }
380
381 if (pinfo->notify_measurements_cmd)
382 dom_info->notify_powercap_measurement_change =
383 SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
384
385
386 dom_info->extended_names = SUPPORTS_EXTENDED_NAMES(flags);
387
388 dom_info->async_powercap_cap_set =
389 SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
390
391 dom_info->powercap_monitoring =
392 SUPPORTS_POWERCAP_MONITORING(flags);
393 dom_info->powercap_scale_mw =
394 SUPPORTS_POWER_UNITS_MW(flags);
395 dom_info->powercap_scale_uw =
396 SUPPORTS_POWER_UNITS_UW(flags);
397 dom_info->fastchannels =
398 SUPPORTS_POWERCAP_FASTCHANNELS(flags);
399
400 strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
401
402 dom_info->sustainable_power =
403 le32_to_cpu(resp->sustainable_power);
404 dom_info->accuracy = le32_to_cpu(resp->accuracy);
405
406 dom_info->parent_id = le32_to_cpu(resp->parent_id);
407 if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
408 (dom_info->parent_id >= pinfo->num_domains ||
409 dom_info->parent_id == dom_info->id)) {
410 dev_err(ph->dev,
411 "Platform reported inconsistent parent ID for domain %d - %s\n",
412 dom_info->id, dom_info->name);
413 return -ENODEV;
414 }
415
416 dom_info->cpli[0].id = CPL0;
417 if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3)
418 dom_info->cpli[0].avg_ivl_config =
419 SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
420 else
421 dom_info->cpli[0].avg_ivl_config =
422 SUPPORTS_POWERCAP_CAI_CONFIGURATION(flags);
423
424 if (PROTOCOL_REV_MAJOR(pinfo->version) < 0x3) {
425 dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_pai);
426 dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_pai);
427 dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->pai_step);
428 } else {
429 struct scmi_msg_resp_powercap_domain_attributes_v3 *resp = r;
430
431 dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_cai);
432 dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_cai);
433 dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->cai_step);
434 }
435
436 ret = scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl,
437 dom_info->cpli[0].max_avg_ivl,
438 dom_info->cpli[0].avg_ivl_step,
439 dom_info->cpli[0].avg_ivl_config);
440 if (ret) {
441 dev_err(ph->dev,
442 "Platform reported inconsistent PAI config for domain %d - %s\n",
443 dom_info->id, dom_info->name);
444 return ret;
445 }
446
447 dom_info->cpli[0].cap_config = cap_config;
448 dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
449 dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
450 dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
451 ret = scmi_powercap_validate(dom_info->cpli[0].min_power_cap,
452 dom_info->cpli[0].max_power_cap,
453 dom_info->cpli[0].power_cap_step,
454 dom_info->cpli[0].cap_config);
455 if (ret) {
456 dev_err(ph->dev,
457 "Platform reported inconsistent CAP config for domain %d - %s\n",
458 dom_info->id, dom_info->name);
459 return ret;
460 }
461 /* Just using same short name */
462 strscpy(dom_info->cpli[0].name, dom_info->name, SCMI_SHORT_NAME_MAX_SIZE);
463
464 return 0;
465 }
466
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout
2025-08-13 11:46 ` [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout Cristian Marussi
@ 2025-08-15 13:13 ` Dan Carpenter
0 siblings, 0 replies; 11+ messages in thread
From: Dan Carpenter @ 2025-08-15 13:13 UTC (permalink / raw)
To: Cristian Marussi
Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm, sudeep.holla,
james.quinlan, f.fainelli, vincent.guittot, etienne.carriere,
peng.fan, michal.simek, quic_sibis, d-gole, souvik.chakravarty
On Wed, Aug 13, 2025 at 12:46:04PM +0100, Cristian Marussi wrote:
> @@ -486,7 +514,7 @@ static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
> }
>
> 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 = ph->get_priv(ph);
> @@ -495,8 +523,11 @@ static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
> return -EINVAL;
>
> dom = pi->powercaps + domain_id;
> - if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
> - *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
> + if (cpl_id > dom->num_cpli)
This gets fixed later in the series but it should be >=.
regards,
dan carpenter
> + return -EINVAL;
> +
> + if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr) {
> + *pai = 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;
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-08-15 13:14 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-13 11:46 [PATCH 0/8] Add support for SCMIv4.0 Powercap Extensions Cristian Marussi
2025-08-13 11:46 ` [PATCH 1/8] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers Cristian Marussi
2025-08-13 11:46 ` [PATCH 2/8] firmware: arm_scmi: Add bound iterators support Cristian Marussi
2025-08-13 11:46 ` [PATCH 3/8] firmware: arm_scmi: Refactor powercap domain layout Cristian Marussi
2025-08-15 13:13 ` Dan Carpenter
2025-08-13 11:46 ` [PATCH 4/8] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support Cristian Marussi
2025-08-14 13:54 ` kernel test robot
2025-08-13 11:46 ` [PATCH 5/8] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support Cristian Marussi
2025-08-13 11:46 ` [PATCH 6/8] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support Cristian Marussi
2025-08-13 11:46 ` [PATCH 7/8] include: trace: Add new parameter to trace_scmi_fc_call Cristian Marussi
2025-08-13 11:46 ` [PATCH 8/8] powercap: arm_scmi: Enable multiple constraints support Cristian Marussi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).