From nobody Sun Feb 8 21:48:39 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8E60AEB64DC for ; Mon, 17 Jul 2023 16:14:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232087AbjGQQNk (ORCPT ); Mon, 17 Jul 2023 12:13:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232127AbjGQQNW (ORCPT ); Mon, 17 Jul 2023 12:13:22 -0400 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 1239511C for ; Mon, 17 Jul 2023 09:13:21 -0700 (PDT) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 3A9F8D75; Mon, 17 Jul 2023 09:14:04 -0700 (PDT) Received: from pluto.. (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 044863F738; Mon, 17 Jul 2023 09:13:18 -0700 (PDT) From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: sudeep.holla@arm.com, james.quinlan@broadcom.com, f.fainelli@gmail.com, vincent.guittot@linaro.org, aidapala@qti.qualcomm.com, souvik.chakravarty@arm.com, satyakim@qti.qualcomm.com, atouzni@qti.qualcomm.com, Cristian Marussi Subject: [PATCH 1/2] firmware: arm_scmi: Harden PERF domain info access Date: Mon, 17 Jul 2023 17:12:45 +0100 Message-ID: <20230717161246.1761777-2-cristian.marussi@arm.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230717161246.1761777-1-cristian.marussi@arm.com> References: <20230717161246.1761777-1-cristian.marussi@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Harden internal accesses to domain info in PERF protocol. Signed-off-by: Cristian Marussi --- drivers/firmware/arm_scmi/perf.c | 89 +++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/p= erf.c index ecf5c4de851b..43dd242ecc49 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -139,7 +139,7 @@ struct perf_dom_info { =20 struct scmi_perf_info { u32 version; - int num_domains; + u16 num_domains; enum scmi_power_scale power_scale; u64 stats_addr; u32 stats_size; @@ -356,11 +356,26 @@ static int scmi_perf_mb_limits_set(const struct scmi_= protocol_handle *ph, return ret; } =20 +static inline struct perf_dom_info * +scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) +{ + struct scmi_perf_info *pi =3D ph->get_priv(ph); + + if (domain >=3D pi->num_domains) + return ERR_PTR(-EINVAL); + + return pi->dom_info + domain; +} + static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf) { struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; + struct perf_dom_info *dom; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 if (PROTOCOL_REV_MAJOR(pi->version) >=3D 0x3 && !max_perf && !min_perf) return -EINVAL; @@ -408,8 +423,11 @@ static int scmi_perf_mb_limits_get(const struct scmi_p= rotocol_handle *ph, static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph, u32 domain, u32 *max_perf, u32 *min_perf) { - struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; + struct perf_dom_info *dom; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].get_addr) { struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; @@ -449,8 +467,11 @@ static int scmi_perf_mb_level_set(const struct scmi_pr= otocol_handle *ph, static int scmi_perf_level_set(const struct scmi_protocol_handle *ph, u32 domain, u32 level, bool poll) { - struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; + struct perf_dom_info *dom; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr) { struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LEVEL]; @@ -490,8 +511,11 @@ static int scmi_perf_mb_level_get(const struct scmi_pr= otocol_handle *ph, static int scmi_perf_level_get(const struct scmi_protocol_handle *ph, u32 domain, u32 *level, bool poll) { - struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; + struct perf_dom_info *dom; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) { *level =3D ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr); @@ -574,13 +598,14 @@ static int scmi_dvfs_device_opps_add(const struct scm= i_protocol_handle *ph, unsigned long freq; struct scmi_opp *opp; struct perf_dom_info *dom; - struct scmi_perf_info *pi =3D ph->get_priv(ph); =20 domain =3D scmi_dev_domain_id(dev); if (domain < 0) - return domain; + return -EINVAL; =20 - dom =3D pi->dom_info + domain; + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 for (opp =3D dom->opp, idx =3D 0; idx < dom->opp_count; idx++, opp++) { freq =3D opp->perf * dom->mult_factor; @@ -603,14 +628,17 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph, struct device *dev) { + int domain; struct perf_dom_info *dom; - struct scmi_perf_info *pi =3D ph->get_priv(ph); - int domain =3D scmi_dev_domain_id(dev); =20 + domain =3D scmi_dev_domain_id(dev); if (domain < 0) - return domain; + return -EINVAL; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 - dom =3D pi->dom_info + domain; /* uS to nS */ return dom->opp[dom->opp_count - 1].trans_latency_us * 1000; } @@ -618,8 +646,11 @@ scmi_dvfs_transition_latency_get(const struct scmi_pro= tocol_handle *ph, static int scmi_dvfs_freq_set(const struct scmi_protocol_handle *ph, u32 d= omain, unsigned long freq, bool poll) { - struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; + struct perf_dom_info *dom; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 return scmi_perf_level_set(ph, domain, freq / dom->mult_factor, poll); } @@ -630,11 +661,14 @@ static int scmi_dvfs_freq_get(const struct scmi_proto= col_handle *ph, u32 domain, int ret; u32 level; struct scmi_perf_info *pi =3D ph->get_priv(ph); - struct perf_dom_info *dom =3D pi->dom_info + domain; =20 ret =3D scmi_perf_level_get(ph, domain, &level, poll); - if (!ret) + if (!ret) { + struct perf_dom_info *dom =3D pi->dom_info + domain; + + /* Note domain is validated implicitly by scmi_perf_level_get */ *freq =3D level * dom->mult_factor; + } =20 return ret; } @@ -643,15 +677,14 @@ static int scmi_dvfs_est_power_get(const struct scmi_= protocol_handle *ph, u32 domain, unsigned long *freq, unsigned long *power) { - struct scmi_perf_info *pi =3D ph->get_priv(ph); struct perf_dom_info *dom; unsigned long opp_freq; int idx, ret =3D -EINVAL; struct scmi_opp *opp; =20 - dom =3D pi->dom_info + domain; - if (!dom) - return -EIO; + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); =20 for (opp =3D dom->opp, idx =3D 0; idx < dom->opp_count; idx++, opp++) { opp_freq =3D opp->perf * dom->mult_factor; @@ -670,10 +703,16 @@ static int scmi_dvfs_est_power_get(const struct scmi_= protocol_handle *ph, static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *p= h, struct device *dev) { + int domain; struct perf_dom_info *dom; - struct scmi_perf_info *pi =3D ph->get_priv(ph); =20 - dom =3D pi->dom_info + scmi_dev_domain_id(dev); + domain =3D scmi_dev_domain_id(dev); + if (domain < 0) + return false; + + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return false; =20 return dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr; } --=20 2.41.0 From nobody Sun Feb 8 21:48:39 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B4D6EC0015E for ; Mon, 17 Jul 2023 16:14:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232100AbjGQQNm (ORCPT ); Mon, 17 Jul 2023 12:13:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59060 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232141AbjGQQNZ (ORCPT ); Mon, 17 Jul 2023 12:13:25 -0400 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 490A211C for ; Mon, 17 Jul 2023 09:13:23 -0700 (PDT) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 717C111FB; Mon, 17 Jul 2023 09:14:06 -0700 (PDT) Received: from pluto.. (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 20DA33F738; Mon, 17 Jul 2023 09:13:20 -0700 (PDT) From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: sudeep.holla@arm.com, james.quinlan@broadcom.com, f.fainelli@gmail.com, vincent.guittot@linaro.org, aidapala@qti.qualcomm.com, souvik.chakravarty@arm.com, satyakim@qti.qualcomm.com, atouzni@qti.qualcomm.com, Cristian Marussi Subject: [PATCH 2/2] firmware: arm_scmi: Add v3.2 PERF Level Indexing mode support Date: Mon, 17 Jul 2023 17:12:46 +0100 Message-ID: <20230717161246.1761777-3-cristian.marussi@arm.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230717161246.1761777-1-cristian.marussi@arm.com> References: <20230717161246.1761777-1-cristian.marussi@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" SCMI v3.2 adds PERF protocol support to optionally define performance domains that cannot be represented on a linear scale; the platform firmware can declare the performance levels of a domain as being 'level indexed' and provide an indicative frequency associated to each of those levels, with such indexes not required anymore to be contiguous nor to satisfy any linear-scaling constraint: when level-indexing is available for a domain, the platform will expect to deal with SCMI requests using indexes instead of performance levels for that domain. Add level-indexing mode support to the PERF protocol implementation while maintaining unchanged the protocol operations interface exposed by PERF; all the required mapping from performamce levels/frequencies to the corresponding level indexes is carried out transparently by the core PERF protocol support: as a consequence no change is either required in any SCMI driver using the PERF protocol, even when using level indexing. Signed-off-by: Cristian Marussi --- drivers/firmware/arm_scmi/perf.c | 367 +++++++++++++++++++++++++------ 1 file changed, 297 insertions(+), 70 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/p= erf.c index 43dd242ecc49..c0cd556fbaae 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -2,19 +2,22 @@ /* * System Control and Management Interface (SCMI) Performance Protocol * - * Copyright (C) 2018-2022 ARM Ltd. + * Copyright (C) 2018-2023 ARM Ltd. */ =20 #define pr_fmt(fmt) "SCMI Notifications PERF - " fmt =20 #include -#include +#include #include +#include #include +#include #include #include #include #include +#include =20 #include =20 @@ -46,6 +49,9 @@ struct scmi_opp { u32 perf; u32 power; u32 trans_latency_us; + u32 indicative_freq; + u32 level_index; + struct hlist_node hash; }; =20 struct scmi_msg_resp_perf_attributes { @@ -66,6 +72,7 @@ struct scmi_msg_resp_perf_domain_attributes { #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) #define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26)) +#define SUPPORTS_LEVEL_INDEXING(x) ((x) & BIT(25)) __le32 rate_limit_us; __le32 sustained_freq_khz; __le32 sustained_perf_level; @@ -122,12 +129,27 @@ struct scmi_msg_resp_perf_describe_levels { } opp[]; }; =20 +struct scmi_msg_resp_perf_describe_levels_v4 { + __le16 num_returned; + __le16 num_remaining; + struct { + __le32 perf_val; + __le32 power; + __le16 transition_latency_us; + __le16 reserved; + __le32 indicative_freq; + __le32 level_index; + } opp[]; +}; + struct perf_dom_info { + u32 id; bool set_limits; bool set_perf; bool perf_limit_notify; bool perf_level_notify; bool perf_fastchannels; + bool level_indexing_mode; u32 opp_count; u32 sustained_freq_khz; u32 sustained_perf_level; @@ -135,8 +157,23 @@ struct perf_dom_info { char name[SCMI_MAX_STR_SIZE]; struct scmi_opp opp[MAX_OPPS]; struct scmi_fc_info *fc_info; + struct xarray opps_by_idx; + struct xarray opps_by_lvl; + DECLARE_HASHTABLE(opps_by_freq, ilog2(MAX_OPPS)); }; =20 +#define LOOKUP_BY_FREQ(__htp, __freq) \ +({ \ + /* u32 cast is needed to pick right hash func */ \ + u32 f_ =3D (u32)(__freq); \ + struct scmi_opp *_opp; \ + \ + hash_for_each_possible((__htp), _opp, hash, f_) \ + if (_opp->indicative_freq =3D=3D f_) \ + break; \ + _opp; \ +}) + struct scmi_perf_info { u32 version; u16 num_domains; @@ -186,9 +223,20 @@ static int scmi_perf_attributes_get(const struct scmi_= protocol_handle *ph, return ret; } =20 +static void scmi_perf_xa_destroy(void *data) +{ + int domain; + struct scmi_perf_info *pinfo =3D data; + + for (domain =3D 0; domain < pinfo->num_domains; domain++) { + xa_destroy(&((pinfo->dom_info + domain)->opps_by_idx)); + xa_destroy(&((pinfo->dom_info + domain)->opps_by_lvl)); + } +} + static int scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct perf_dom_info *dom_info, + struct perf_dom_info *dom_info, u32 version) { int ret; @@ -197,11 +245,11 @@ scmi_perf_domain_attributes_get(const struct scmi_pro= tocol_handle *ph, struct scmi_msg_resp_perf_domain_attributes *attr; =20 ret =3D ph->xops->xfer_get_init(ph, PERF_DOMAIN_ATTRIBUTES, - sizeof(domain), sizeof(*attr), &t); + sizeof(dom_info->id), sizeof(*attr), &t); if (ret) return ret; =20 - put_unaligned_le32(domain, t->tx.buf); + put_unaligned_le32(dom_info->id, t->tx.buf); attr =3D t->rx.buf; =20 ret =3D ph->xops->do_xfer(ph, t); @@ -213,6 +261,9 @@ scmi_perf_domain_attributes_get(const struct scmi_proto= col_handle *ph, dom_info->perf_limit_notify =3D SUPPORTS_PERF_LIMIT_NOTIFY(flags); dom_info->perf_level_notify =3D SUPPORTS_PERF_LEVEL_NOTIFY(flags); dom_info->perf_fastchannels =3D SUPPORTS_PERF_FASTCHANNELS(flags); + if (PROTOCOL_REV_MAJOR(version) >=3D 0x4) + dom_info->level_indexing_mode =3D + SUPPORTS_LEVEL_INDEXING(flags); dom_info->sustained_freq_khz =3D le32_to_cpu(attr->sustained_freq_khz); dom_info->sustained_perf_level =3D @@ -236,8 +287,15 @@ scmi_perf_domain_attributes_get(const struct scmi_prot= ocol_handle *ph, */ if (!ret && PROTOCOL_REV_MAJOR(version) >=3D 0x3 && SUPPORTS_EXTENDED_NAMES(flags)) - ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, domain, - dom_info->name, SCMI_MAX_STR_SIZE); + ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, + dom_info->id, dom_info->name, + SCMI_MAX_STR_SIZE); + + if (dom_info->level_indexing_mode) { + xa_init(&dom_info->opps_by_idx); + xa_init(&dom_info->opps_by_lvl); + hash_init(dom_info->opps_by_freq); + } =20 return ret; } @@ -250,7 +308,7 @@ static int opp_cmp_func(const void *opp1, const void *o= pp2) } =20 struct scmi_perf_ipriv { - u32 domain; + u32 version; struct perf_dom_info *perf_dom; }; =20 @@ -261,7 +319,7 @@ static void iter_perf_levels_prepare_message(void *mess= age, struct scmi_msg_perf_describe_levels *msg =3D message; const struct scmi_perf_ipriv *p =3D priv; =20 - msg->domain =3D cpu_to_le32(p->domain); + msg->domain =3D cpu_to_le32(p->perf_dom->id); /* Set the number of OPPs to be skipped/already read */ msg->level_index =3D cpu_to_le32(desc_index); } @@ -277,31 +335,63 @@ static int iter_perf_levels_update_state(struct scmi_= iterator_state *st, return 0; } =20 +static inline void +process_response_opp(struct scmi_opp *opp, unsigned int loop_idx, + const struct scmi_msg_resp_perf_describe_levels *r) +{ + opp->perf =3D le32_to_cpu(r->opp[loop_idx].perf_val); + opp->power =3D le32_to_cpu(r->opp[loop_idx].power); + opp->trans_latency_us =3D + le16_to_cpu(r->opp[loop_idx].transition_latency_us); +} + +static inline void +process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp, + unsigned int loop_idx, + const struct scmi_msg_resp_perf_describe_levels_v4 *r) +{ + opp->perf =3D le32_to_cpu(r->opp[loop_idx].perf_val); + opp->power =3D le32_to_cpu(r->opp[loop_idx].power); + opp->trans_latency_us =3D + le16_to_cpu(r->opp[loop_idx].transition_latency_us); + + /* Note that PERF v4 reports always five 32-bit words */ + opp->indicative_freq =3D le32_to_cpu(r->opp[loop_idx].indicative_freq); + if (dom->level_indexing_mode) { + opp->level_index =3D le32_to_cpu(r->opp[loop_idx].level_index); + + xa_store(&dom->opps_by_idx, opp->level_index, opp, GFP_KERNEL); + xa_store(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq); + } +} + static int iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, const void *response, struct scmi_iterator_state *st, void *priv) { struct scmi_opp *opp; - const struct scmi_msg_resp_perf_describe_levels *r =3D response; struct scmi_perf_ipriv *p =3D priv; =20 opp =3D &p->perf_dom->opp[st->desc_index + st->loop_idx]; - opp->perf =3D le32_to_cpu(r->opp[st->loop_idx].perf_val); - opp->power =3D le32_to_cpu(r->opp[st->loop_idx].power); - opp->trans_latency_us =3D - le16_to_cpu(r->opp[st->loop_idx].transition_latency_us); + if (PROTOCOL_REV_MAJOR(p->version) <=3D 0x3) + process_response_opp(opp, st->loop_idx, response); + else + process_response_opp_v4(p->perf_dom, opp, st->loop_idx, + response); p->perf_dom->opp_count++; =20 - dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n", - opp->perf, opp->power, opp->trans_latency_us); + dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n", + opp->perf, opp->power, opp->trans_latency_us, + opp->indicative_freq, opp->level_index); =20 return 0; } =20 static int -scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 d= omain, - struct perf_dom_info *perf_dom) +scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *perf_dom, u32 version) { int ret; void *iter; @@ -311,7 +401,7 @@ scmi_perf_describe_levels_get(const struct scmi_protoco= l_handle *ph, u32 domain, .process_response =3D iter_perf_levels_process_response, }; struct scmi_perf_ipriv ppriv =3D { - .domain =3D domain, + .version =3D version, .perf_dom =3D perf_dom, }; =20 @@ -333,8 +423,8 @@ scmi_perf_describe_levels_get(const struct scmi_protoco= l_handle *ph, u32 domain, return ret; } =20 -static int scmi_perf_mb_limits_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 max_perf, u32 min_perf) +static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph, + u32 domain, u32 max_perf, u32 min_perf) { int ret; struct scmi_xfer *t; @@ -367,6 +457,24 @@ scmi_perf_domain_lookup(const struct scmi_protocol_han= dle *ph, u32 domain) return pi->dom_info + domain; } =20 +static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 max_perf, + u32 min_perf) +{ + if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].set_addr) { + struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; + + trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET, + dom->id, 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); + return 0; + } + + return scmi_perf_msg_limits_set(ph, dom->id, max_perf, min_perf); +} + static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf) { @@ -380,22 +488,31 @@ static int scmi_perf_limits_set(const struct scmi_pro= tocol_handle *ph, if (PROTOCOL_REV_MAJOR(pi->version) >=3D 0x3 && !max_perf && !min_perf) return -EINVAL; =20 - if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].set_addr) { - struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; + if (dom->level_indexing_mode) { + struct scmi_opp *opp; =20 - trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET, - domain, 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); - return 0; + if (min_perf) { + opp =3D xa_load(&dom->opps_by_lvl, min_perf); + if (!opp) + return -EIO; + + min_perf =3D opp->level_index; + } + + if (max_perf) { + opp =3D xa_load(&dom->opps_by_lvl, max_perf); + if (!opp) + return -EIO; + + max_perf =3D opp->level_index; + } } =20 - return scmi_perf_mb_limits_set(ph, domain, max_perf, min_perf); + return __scmi_perf_limits_set(ph, dom, max_perf, min_perf); } =20 -static int scmi_perf_mb_limits_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *max_perf, u32 *min_perf) +static int scmi_perf_msg_limits_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *max_perf, u32 *min_perf) { int ret; struct scmi_xfer *t; @@ -420,30 +537,58 @@ static int scmi_perf_mb_limits_get(const struct scmi_= protocol_handle *ph, return ret; } =20 +static int __scmi_perf_limits_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 *max_perf, + u32 *min_perf) +{ + if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].get_addr) { + struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; + + *max_perf =3D ioread32(fci->get_addr); + *min_perf =3D ioread32(fci->get_addr + 4); + trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET, + dom->id, *min_perf, *max_perf); + return 0; + } + + return scmi_perf_msg_limits_get(ph, dom->id, max_perf, min_perf); +} + static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph, u32 domain, u32 *max_perf, u32 *min_perf) { + int ret; struct perf_dom_info *dom; =20 dom =3D scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); =20 - if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].get_addr) { - struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LIMIT]; + ret =3D __scmi_perf_limits_get(ph, dom, max_perf, min_perf); + if (ret) + return ret; =20 - *max_perf =3D ioread32(fci->get_addr); - *min_perf =3D ioread32(fci->get_addr + 4); - trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET, - domain, *min_perf, *max_perf); - return 0; + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + opp =3D xa_load(&dom->opps_by_idx, *min_perf); + if (!opp) + return -EIO; + + *min_perf =3D opp->perf; + + opp =3D xa_load(&dom->opps_by_idx, *max_perf); + if (!opp) + return -EIO; + + *max_perf =3D opp->perf; } =20 - return scmi_perf_mb_limits_get(ph, domain, max_perf, min_perf); + return 0; } =20 -static int scmi_perf_mb_level_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 level, bool poll) +static int scmi_perf_msg_level_set(const struct scmi_protocol_handle *ph, + u32 domain, u32 level, bool poll) { int ret; struct scmi_xfer *t; @@ -464,6 +609,23 @@ static int scmi_perf_mb_level_set(const struct scmi_pr= otocol_handle *ph, return ret; } =20 +static int __scmi_perf_level_set(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 level, + bool poll) +{ + if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr) { + struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LEVEL]; + + trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET, + dom->id, level, 0); + iowrite32(level, fci->set_addr); + ph->hops->fastchannel_db_ring(fci->set_db); + return 0; + } + + return scmi_perf_msg_level_set(ph, dom->id, level, poll); +} + static int scmi_perf_level_set(const struct scmi_protocol_handle *ph, u32 domain, u32 level, bool poll) { @@ -473,21 +635,21 @@ static int scmi_perf_level_set(const struct scmi_prot= ocol_handle *ph, if (IS_ERR(dom)) return PTR_ERR(dom); =20 - if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr) { - struct scmi_fc_info *fci =3D &dom->fc_info[PERF_FC_LEVEL]; + if (dom->level_indexing_mode) { + struct scmi_opp *opp; =20 - trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET, - domain, level, 0); - iowrite32(level, fci->set_addr); - ph->hops->fastchannel_db_ring(fci->set_db); - return 0; + opp =3D xa_load(&dom->opps_by_lvl, level); + if (!opp) + return -EIO; + + level =3D opp->level_index; } =20 - return scmi_perf_mb_level_set(ph, domain, level, poll); + return __scmi_perf_level_set(ph, dom, level, poll); } =20 -static int scmi_perf_mb_level_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *level, bool poll) +static int scmi_perf_msg_level_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *level, bool poll) { int ret; struct scmi_xfer *t; @@ -508,23 +670,45 @@ static int scmi_perf_mb_level_get(const struct scmi_p= rotocol_handle *ph, return ret; } =20 +static int __scmi_perf_level_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 *level, + bool poll) +{ + if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) { + *level =3D ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr); + trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET, + dom->id, *level, 0); + return 0; + } + + return scmi_perf_msg_level_get(ph, dom->id, level, poll); +} + static int scmi_perf_level_get(const struct scmi_protocol_handle *ph, u32 domain, u32 *level, bool poll) { + int ret; struct perf_dom_info *dom; =20 dom =3D scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); =20 - if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) { - *level =3D ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr); - trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET, - domain, *level, 0); - return 0; + ret =3D __scmi_perf_level_get(ph, dom, level, poll); + if (ret) + return ret; + + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + opp =3D xa_load(&dom->opps_by_idx, *level); + if (!opp) + return -EIO; + + *level =3D opp->perf; } =20 - return scmi_perf_mb_level_get(ph, domain, level, poll); + return 0; } =20 static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle= *ph, @@ -608,18 +792,27 @@ static int scmi_dvfs_device_opps_add(const struct scm= i_protocol_handle *ph, return PTR_ERR(dom); =20 for (opp =3D dom->opp, idx =3D 0; idx < dom->opp_count; idx++, opp++) { - freq =3D opp->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + freq =3D opp->perf * dom->mult_factor; + else + freq =3D opp->indicative_freq * 1000; =20 ret =3D dev_pm_opp_add(dev, freq, 0); if (ret) { dev_warn(dev, "failed to add opp %luHz\n", freq); =20 while (idx-- > 0) { - freq =3D (--opp)->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + freq =3D (--opp)->perf * dom->mult_factor; + else + freq =3D (--opp)->indicative_freq * 1000; dev_pm_opp_remove(dev, freq); } return ret; } + + dev_dbg(dev, "[%d][%s]:: Registered OPP[%d] %lu\n", + domain, dom->name, idx, freq); } return 0; } @@ -646,13 +839,26 @@ scmi_dvfs_transition_latency_get(const struct scmi_pr= otocol_handle *ph, static int scmi_dvfs_freq_set(const struct scmi_protocol_handle *ph, u32 d= omain, unsigned long freq, bool poll) { + unsigned int level; struct perf_dom_info *dom; =20 dom =3D scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); =20 - return scmi_perf_level_set(ph, domain, freq / dom->mult_factor, poll); + if (!dom->level_indexing_mode) { + level =3D freq / dom->mult_factor; + } else { + struct scmi_opp *opp; + + opp =3D LOOKUP_BY_FREQ(dom->opps_by_freq, freq / 1000); + if (!opp) + return -EIO; + + level =3D opp->level_index; + } + + return __scmi_perf_level_set(ph, dom, level, poll); } =20 static int scmi_dvfs_freq_get(const struct scmi_protocol_handle *ph, u32 d= omain, @@ -660,14 +866,26 @@ static int scmi_dvfs_freq_get(const struct scmi_proto= col_handle *ph, u32 domain, { int ret; u32 level; - struct scmi_perf_info *pi =3D ph->get_priv(ph); + struct perf_dom_info *dom; =20 - ret =3D scmi_perf_level_get(ph, domain, &level, poll); - if (!ret) { - struct perf_dom_info *dom =3D pi->dom_info + domain; + dom =3D scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + ret =3D __scmi_perf_level_get(ph, dom, &level, poll); + if (ret) + return ret; =20 - /* Note domain is validated implicitly by scmi_perf_level_get */ + if (!dom->level_indexing_mode) { *freq =3D level * dom->mult_factor; + } else { + struct scmi_opp *opp; + + opp =3D xa_load(&dom->opps_by_idx, level); + if (!opp) + return -EIO; + + *freq =3D opp->indicative_freq * 1000; } =20 return ret; @@ -687,7 +905,11 @@ static int scmi_dvfs_est_power_get(const struct scmi_p= rotocol_handle *ph, return PTR_ERR(dom); =20 for (opp =3D dom->opp, idx =3D 0; idx < dom->opp_count; idx++, opp++) { - opp_freq =3D opp->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + opp_freq =3D opp->perf * dom->mult_factor; + else + opp_freq =3D opp->indicative_freq * 1000; + if (opp_freq < *freq) continue; =20 @@ -870,13 +1092,18 @@ static int scmi_perf_protocol_init(const struct scmi= _protocol_handle *ph) for (domain =3D 0; domain < pinfo->num_domains; domain++) { struct perf_dom_info *dom =3D pinfo->dom_info + domain; =20 - scmi_perf_domain_attributes_get(ph, domain, dom, version); - scmi_perf_describe_levels_get(ph, domain, dom); + dom->id =3D domain; + scmi_perf_domain_attributes_get(ph, dom, version); + scmi_perf_describe_levels_get(ph, dom, version); =20 if (dom->perf_fastchannels) - scmi_perf_domain_init_fc(ph, domain, &dom->fc_info); + scmi_perf_domain_init_fc(ph, dom->id, &dom->fc_info); } =20 + ret =3D devm_add_action_or_reset(ph->dev, scmi_perf_xa_destroy, pinfo); + if (ret) + return ret; + pinfo->version =3D version; =20 return ph->set_priv(ph, pinfo); --=20 2.41.0