firmware: arm_scmi: Use bound iterators to minimize discovered rates

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>
This commit is contained in:
Cristian Marussi 2026-05-08 16:32:58 +01:00 committed by Sudeep Holla
parent 3065e26dac
commit 26d04d592a

View 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;
}