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
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
© 2016 - 2025 Red Hat, Inc.