]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_scmi: Use bound iterators to minimize discovered rates
authorCristian Marussi <cristian.marussi@arm.com>
Fri, 8 May 2026 15:32:58 +0000 (16:32 +0100)
committerSudeep Holla <sudeep.holla@kernel.org>
Tue, 12 May 2026 14:29:11 +0000 (15:29 +0100)
Clock rates are guaranteed to be returned in ascending order for SCMI clock
protocol versions greater than 1.0: in such a case, use bounded iterators
to minimize the number of message exchanges needed to discover min and max
rate.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://patch.msgid.link/20260508153300.2224715-14-cristian.marussi@arm.com
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
drivers/firmware/arm_scmi/clock.c

index 8ce889dfc87b7fa733bb18689f19234b21f0a3ea..955bb9565ce31d7a12aec2bb5b0e596355f0dc08 100644 (file)
@@ -160,6 +160,7 @@ struct scmi_clock_rate_notify_payld {
 struct scmi_clock_desc {
        u32 id;
        bool rate_discrete;
+       unsigned int tot_rates;
        unsigned int num_rates;
        u64 *rates;
 #define        RATE_MIN        0
@@ -483,15 +484,16 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st,
        }
 
        if (!st->max_resources) {
-               int num_rates = st->num_returned + st->num_remaining;
+               unsigned int tot_rates = st->num_returned + st->num_remaining;
 
-               p->clkd->rates = devm_kcalloc(p->dev, num_rates,
+               p->clkd->rates = devm_kcalloc(p->dev, tot_rates,
                                              sizeof(*p->clkd->rates), GFP_KERNEL);
                if (!p->clkd->rates)
                        return -ENOMEM;
 
                /* max_resources is used by the iterators to control bounds */
-               st->max_resources = st->num_returned + st->num_remaining;
+               p->clkd->tot_rates = tot_rates;
+               st->max_resources = tot_rates;
        }
 
        return 0;
@@ -514,8 +516,8 @@ iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
 }
 
 static int
-scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
-                             struct clock_info *cinfo)
+scmi_clock_describe_rates_get_full(const struct scmi_protocol_handle *ph,
+                                  struct scmi_clock_desc *clkd)
 {
        int ret;
        void *iter;
@@ -524,7 +526,6 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
                .update_state = iter_clk_describe_update_state,
                .process_response = iter_clk_describe_process_response,
        };
-       struct scmi_clock_desc *clkd = &cinfo->clkds[clk_id];
        struct scmi_clk_ipriv cpriv = {
                .clkd = clkd,
                .dev = ph->dev,
@@ -544,17 +545,88 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
        if (!clkd->num_rates)
                return 0;
 
+       if (clkd->rate_discrete)
+               sort(clkd->rates, clkd->num_rates,
+                    sizeof(clkd->rates[0]), rate_cmp_func, NULL);
+
+       return 0;
+}
+
+static int
+scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph,
+                                  struct scmi_clock_desc *clkd)
+{
+       struct scmi_iterator_ops ops = {
+               .prepare_message = iter_clk_describe_prepare_message,
+               .update_state = iter_clk_describe_update_state,
+               .process_response = iter_clk_describe_process_response,
+       };
+       struct scmi_clk_ipriv cpriv = {
+               .clkd = clkd,
+               .dev = ph->dev,
+       };
+       unsigned int first, last;
+       void *iter;
+       int ret;
+
+       iter = ph->hops->iter_response_init(ph, &ops, 0, CLOCK_DESCRIBE_RATES,
+                                           sizeof(struct scmi_msg_clock_describe_rates),
+                                           &cpriv);
+       if (IS_ERR(iter))
+               return PTR_ERR(iter);
+
+       /* Try to grab a triplet, so that in case is NON-discrete we are done */
+       first = 0;
+       last = 2;
+       ret = ph->hops->iter_response_run_bound(iter, &first, &last);
+       if (ret)
+               goto out;
+
+       /* If discrete grab the last value, which should be the max */
+       if (clkd->rate_discrete && clkd->tot_rates > 3) {
+               first = clkd->tot_rates - 1;
+               last = clkd->tot_rates - 1;
+               ret = ph->hops->iter_response_run_bound(iter, &first, &last);
+       }
+
+out:
+       ph->hops->iter_response_bound_cleanup(iter);
+
+       return ret;
+}
+
+static int
+scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph,
+                             u32 clk_id, struct clock_info *cinfo)
+{
+       struct scmi_clock_desc *clkd = &cinfo->clkds[clk_id];
+       int ret;
+
+       /*
+        * Since only after SCMI Clock v1.0 the returned rates are guaranteed to
+        * be discovered in ascending order, lazy enumeration cannot be use for
+        * SCMI Clock v1.0 protocol.
+        */
+       if (PROTOCOL_REV_MAJOR(ph->version) > 0x1)
+               ret = scmi_clock_describe_rates_get_lazy(ph, clkd);
+       else
+               ret = scmi_clock_describe_rates_get_full(ph, clkd);
+
+       if (ret)
+               return ret;
+
+       clkd->info.min_rate = clkd->rates[RATE_MIN];
        if (!clkd->rate_discrete) {
                clkd->info.max_rate = clkd->rates[RATE_MAX];
                dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
                        clkd->rates[RATE_MIN], clkd->rates[RATE_MAX],
                        clkd->rates[RATE_STEP]);
        } else {
-               sort(clkd->rates, clkd->num_rates,
-                    sizeof(clkd->rates[0]), rate_cmp_func, NULL);
                clkd->info.max_rate = clkd->rates[clkd->num_rates - 1];
+               dev_dbg(ph->dev, "Clock:%s Num_Rates:%u -> Min %llu Max %llu\n",
+                       clkd->info.name, clkd->tot_rates,
+                       clkd->info.min_rate, clkd->info.max_rate);
        }
-       clkd->info.min_rate = clkd->rates[RATE_MIN];
 
        return 0;
 }