[PATCH 11/11] firmware: arm_scmi: Introduce all_rates_get clock operation
From: Cristian Marussi <cristian.marussi@arm.com>
Date: 2026-02-27 15:34:18
Also in:
arm-scmi, linux-clk, linux-renesas-soc, lkml
Subsystem:
system control & power/management interface (scpi/scmi) message protocol drivers, the rest · Maintainers:
Sudeep Holla, Linus Torvalds
Add a clock operation to get the whole set of rates available to a specific clock: when needed this request could transparently trigger a full rate discovery enumeration if this specific clock-rates were previously only lazily enumerated. Signed-off-by: Cristian Marussi <cristian.marussi@arm.com> --- drivers/firmware/arm_scmi/clock.c | 85 +++++++++++++++++++++---------- include/linux/scmi_protocol.h | 9 ++++ 2 files changed, 67 insertions(+), 27 deletions(-)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index a0de10652abe..c2fd9a1c3316 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c@@ -159,10 +159,8 @@ 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; + struct scmi_clock_rates r; #define RATE_MIN 0 #define RATE_MAX 1 #define RATE_STEP 2
@@ -469,10 +467,10 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st, flags = le32_to_cpu(r->num_rates_flags); st->num_remaining = NUM_REMAINING(flags); st->num_returned = NUM_RETURNED(flags); - p->clkd->rate_discrete = RATE_DISCRETE(flags); + p->clkd->r.rate_discrete = RATE_DISCRETE(flags); /* Warn about out of spec replies ... */ - if (!p->clkd->rate_discrete && + if (!p->clkd->r.rate_discrete && (st->num_returned != 3 || st->num_remaining != 0)) { dev_warn(p->dev, "Out-of-spec CLOCK_DESCRIBE_RATES reply for %s - returned:%d remaining:%d rx_len:%zd\n",
@@ -486,9 +484,9 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st, if (!st->max_resources) { unsigned int tot_rates = st->num_returned + st->num_remaining; - p->clkd->rates = devm_kcalloc(p->dev, tot_rates, - sizeof(*p->clkd->rates), GFP_KERNEL); - if (!p->clkd->rates) + p->clkd->r.rates = devm_kcalloc(p->dev, tot_rates, + sizeof(*p->clkd->r.rates), GFP_KERNEL); + if (!p->clkd->r.rates) return -ENOMEM; /* max_resources is used by the iterators to control bounds */
@@ -507,10 +505,10 @@ iter_clk_describe_process_response(const struct scmi_protocol_handle *ph, struct scmi_clk_ipriv *p = priv; const struct scmi_msg_resp_clock_describe_rates *r = response; - p->clkd->rates[p->clkd->num_rates] = RATE_TO_U64(r->rate[st->loop_idx]); + p->clkd->r.rates[p->clkd->r.num_rates] = RATE_TO_U64(r->rate[st->loop_idx]); /* Count only effectively discovered rates */ - p->clkd->num_rates++; + p->clkd->r.num_rates++; return 0; }
@@ -531,7 +529,13 @@ scmi_clock_describe_rates_get_full(const struct scmi_protocol_handle *ph, .dev = ph->dev, }; - iter = ph->hops->iter_response_init(ph, &ops, 0, CLOCK_DESCRIBE_RATES, + /* + * Using tot_rates as max_resources parameter here so as to trigger + * the dynamic allocation only when strictly needed: when trying a + * full enumeration after a lazy one tot_rates will be non-zero. + */ + iter = ph->hops->iter_response_init(ph, &ops, clkd->tot_rates, + CLOCK_DESCRIBE_RATES, sizeof(struct scmi_msg_clock_describe_rates), &cpriv); if (IS_ERR(iter))
@@ -542,12 +546,12 @@ scmi_clock_describe_rates_get_full(const struct scmi_protocol_handle *ph, return ret; /* empty set ? */ - if (!clkd->num_rates) + if (!clkd->r.num_rates) return 0; - if (clkd->rate_discrete) - sort(clkd->rates, clkd->num_rates, - sizeof(clkd->rates[0]), rate_cmp_func, NULL); + if (clkd->r.rate_discrete && PROTOCOL_REV_MAJOR(ph->version) == 0x1) + sort(clkd->r.rates, clkd->r.num_rates, + sizeof(clkd->r.rates[0]), rate_cmp_func, NULL); return 0; }
@@ -583,7 +587,7 @@ scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph, goto out; /* If discrete grab the last value, which should be the max */ - if (clkd->rate_discrete && clkd->tot_rates > 3) { + if (clkd->r.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);
@@ -615,16 +619,16 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, if (ret) return ret; - clkd->info.min_rate = clkd->rates[RATE_MIN]; - if (!clkd->rate_discrete) { - clkd->info.max_rate = clkd->rates[RATE_MAX]; + clkd->info.min_rate = clkd->r.rates[RATE_MIN]; + if (!clkd->r.rate_discrete) { + clkd->info.max_rate = clkd->r.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]); + clkd->r.rates[RATE_MIN], clkd->r.rates[RATE_MAX], + clkd->r.rates[RATE_STEP]); } else { - clkd->info.max_rate = clkd->rates[clkd->num_rates - 1]; + clkd->info.max_rate = clkd->r.rates[clkd->r.num_rates - 1]; dev_dbg(ph->dev, "Clock:%s DISCRETE:%d -> Min %llu Max %llu\n", - clkd->info.name, clkd->rate_discrete, + clkd->info.name, clkd->r.rate_discrete, clkd->info.min_rate, clkd->info.max_rate); }
@@ -729,7 +733,7 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, * If we can't figure out what rate it will be, so just return the * rate back to the caller. */ - if (clkd->rate_discrete) + if (clkd->r.rate_discrete) return 0; fmin = clk->min_rate;
@@ -743,14 +747,40 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, } ftmp = *rate - fmin; - ftmp += clkd->rates[RATE_STEP] - 1; /* to round up */ - do_div(ftmp, clkd->rates[RATE_STEP]); + ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */ + do_div(ftmp, clkd->r.rates[RATE_STEP]); - *rate = ftmp * clkd->rates[RATE_STEP] + fmin; + *rate = ftmp * clkd->r.rates[RATE_STEP] + fmin; return 0; } +static const struct scmi_clock_rates * +scmi_clock_all_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id) +{ + struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_desc *clkd; + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk) || !clk->name[0]) + return NULL; + + clkd = to_desc(clk); + /* Needs full enumeration ? */ + if (clkd->r.rate_discrete && clkd->tot_rates != clkd->r.num_rates) { + int ret; + + /* rates[] is already allocated BUT we need to re-enumerate */ + clkd->r.num_rates = 0; + ret = scmi_clock_describe_rates_get_full(ph, clkd); + if (ret) + return NULL; + } + + return &clkd->r; +} + static int scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, enum clk_state state,
@@ -1064,6 +1094,7 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .rate_get = scmi_clock_rate_get, .rate_set = scmi_clock_rate_set, .determine_rate = scmi_clock_determine_rate, + .all_rates_get = scmi_clock_all_rates_get, .enable = scmi_clock_enable, .disable = scmi_clock_disable, .state_get = scmi_clock_state_get,
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 5552ac04c820..c710107c2120 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h@@ -40,6 +40,12 @@ struct scmi_revision_info { char sub_vendor_id[SCMI_SHORT_NAME_MAX_SIZE]; }; +struct scmi_clock_rates { + bool rate_discrete; + unsigned int num_rates; + u64 *rates; +}; + struct scmi_clock_info { char name[SCMI_MAX_STR_SIZE]; unsigned int enable_latency;
@@ -85,6 +91,7 @@ enum scmi_clock_oem_config { * clock calculating the closest allowed rate. * Note that @rate is an input/output parameter used both to * describe the requested rate and report the closest match + * @all_rates_get: get the list of all available rates for the specified clock. * @enable: enables the specified clock * @disable: disables the specified clock * @state_get: get the status of the specified clock
@@ -104,6 +111,8 @@ struct scmi_clk_proto_ops { u64 rate); int (*determine_rate)(const struct scmi_protocol_handle *ph, u32 clk_id, unsigned long *rate); + const struct scmi_clock_rates __must_check *(*all_rates_get) + (const struct scmi_protocol_handle *ph, u32 clk_id); int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic); int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id,
--
2.53.0