firmware: arm_scmi: Simplify clock rates exposed interface

Introduce a new internal struct scmi_clock_desc so as to be able to hide,
in the future, some of the needlessly public fields currently kept inside
scmi_clock_info, while keeping exposed only the two new min_rate and
max_rate fields for each clock.

No functional change.

Reviewed-by: Peng Fan <peng.fan@nxp.com>
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Tested-by: Florian Fainelli <florian.fainelli@broadcom.com>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/20260508153300.2224715-5-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:49 +01:00 committed by Sudeep Holla
parent af86c99170
commit 0d76f62613
2 changed files with 85 additions and 75 deletions

View File

@ -157,13 +157,27 @@ struct scmi_clock_rate_notify_payld {
__le32 rate_high;
};
struct scmi_clock_desc {
u32 id;
bool rate_discrete;
unsigned int num_rates;
u64 rates[SCMI_MAX_NUM_RATES];
#define RATE_MIN 0
#define RATE_MAX 1
#define RATE_STEP 2
struct scmi_clock_info info;
};
#define to_desc(p) (container_of(p, struct scmi_clock_desc, info))
struct clock_info {
int num_clocks;
int max_async_req;
bool notify_rate_changed_cmd;
bool notify_rate_change_requested_cmd;
atomic_t cur_async_req;
struct scmi_clock_info *clk;
struct scmi_clock_desc *clkds;
#define CLOCK_INFO(c, i) (&(((c)->clkds + (i))->info))
int (*clock_config_set)(const struct scmi_protocol_handle *ph,
u32 clk_id, enum clk_state state,
enum scmi_clock_oem_config oem_type,
@ -185,7 +199,7 @@ scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
if (clk_id >= ci->num_clocks)
return ERR_PTR(-EINVAL);
return ci->clk + clk_id;
return CLOCK_INFO(ci, clk_id);
}
static int
@ -226,8 +240,7 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
struct scmi_clk_ipriv {
struct device *dev;
u32 clk_id;
struct scmi_clock_info *clk;
struct scmi_clock_desc *clkd;
};
static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index,
@ -236,7 +249,7 @@ static void iter_clk_possible_parents_prepare_message(void *message, unsigned in
struct scmi_msg_clock_possible_parents *msg = message;
const struct scmi_clk_ipriv *p = priv;
msg->id = cpu_to_le32(p->clk_id);
msg->id = cpu_to_le32(p->clkd->id);
/* Set the number of OPPs to be skipped/already read */
msg->skip_parents = cpu_to_le32(desc_index);
}
@ -246,7 +259,6 @@ static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st
{
const struct scmi_msg_resp_clock_possible_parents *r = response;
struct scmi_clk_ipriv *p = priv;
struct device *dev = ((struct scmi_clk_ipriv *)p)->dev;
u32 flags;
flags = le32_to_cpu(r->num_parent_flags);
@ -258,12 +270,13 @@ static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st
* assume it's returned+remaining on first call.
*/
if (!st->max_resources) {
p->clk->num_parents = st->num_returned + st->num_remaining;
p->clk->parents = devm_kcalloc(dev, p->clk->num_parents,
sizeof(*p->clk->parents),
GFP_KERNEL);
if (!p->clk->parents) {
p->clk->num_parents = 0;
p->clkd->info.num_parents = st->num_returned + st->num_remaining;
p->clkd->info.parents = devm_kcalloc(p->dev,
p->clkd->info.num_parents,
sizeof(*p->clkd->info.parents),
GFP_KERNEL);
if (!p->clkd->info.parents) {
p->clkd->info.num_parents = 0;
return -ENOMEM;
}
st->max_resources = st->num_returned + st->num_remaining;
@ -280,29 +293,27 @@ static int iter_clk_possible_parents_process_response(const struct scmi_protocol
const struct scmi_msg_resp_clock_possible_parents *r = response;
struct scmi_clk_ipriv *p = priv;
u32 *parent = &p->clk->parents[st->desc_index + st->loop_idx];
u32 *parent = &p->clkd->info.parents[st->desc_index + st->loop_idx];
*parent = le32_to_cpu(r->possible_parents[st->loop_idx]);
return 0;
}
static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u32 clk_id,
struct scmi_clock_info *clk)
static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph,
u32 clk_id, struct clock_info *cinfo)
{
struct scmi_iterator_ops ops = {
.prepare_message = iter_clk_possible_parents_prepare_message,
.update_state = iter_clk_possible_parents_update_state,
.process_response = iter_clk_possible_parents_process_response,
};
struct scmi_clock_desc *clkd = &cinfo->clkds[clk_id];
struct scmi_clk_ipriv ppriv = {
.clk_id = clk_id,
.clk = clk,
.clkd = clkd,
.dev = ph->dev,
};
void *iter;
int ret;
iter = ph->hops->iter_response_init(ph, &ops, 0,
CLOCK_POSSIBLE_PARENTS_GET,
@ -311,9 +322,7 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
if (IS_ERR(iter))
return PTR_ERR(iter);
ret = ph->hops->iter_response_run(iter);
return ret;
return ph->hops->iter_response_run(iter);
}
static int
@ -352,7 +361,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_clock_attributes *attr;
struct scmi_clock_info *clk = cinfo->clk + clk_id;
struct scmi_clock_info *clk = CLOCK_INFO(cinfo, clk_id);
ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
sizeof(clk_id), sizeof(*attr), &t);
@ -394,7 +403,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
clk->rate_change_requested_notifications = true;
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
if (SUPPORTS_PARENT_CLOCK(attributes))
scmi_clock_possible_parents(ph, clk_id, clk);
scmi_clock_possible_parents(ph, clk_id, cinfo);
if (SUPPORTS_GET_PERMISSIONS(attributes))
scmi_clock_get_permissions(ph, clk_id, clk);
if (SUPPORTS_EXTENDED_CONFIG(attributes))
@ -424,7 +433,7 @@ static void iter_clk_describe_prepare_message(void *message,
struct scmi_msg_clock_describe_rates *msg = message;
const struct scmi_clk_ipriv *p = priv;
msg->id = cpu_to_le32(p->clk_id);
msg->id = cpu_to_le32(p->clkd->id);
/* Set the number of rates to be skipped/already read */
msg->rate_index = cpu_to_le32(desc_index);
}
@ -457,14 +466,15 @@ 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->clk->rate_discrete = RATE_DISCRETE(flags);
p->clkd->rate_discrete = RATE_DISCRETE(flags);
p->clkd->info.rate_discrete = p->clkd->rate_discrete;
/* Warn about out of spec replies ... */
if (!p->clk->rate_discrete &&
if (!p->clkd->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",
p->clk->name, st->num_returned, st->num_remaining,
p->clkd->info.name, st->num_returned, st->num_remaining,
st->rx_len);
SCMI_QUIRK(clock_rates_triplet_out_of_spec,
@ -479,38 +489,19 @@ iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv)
{
int ret = 0;
struct scmi_clk_ipriv *p = priv;
const struct scmi_msg_resp_clock_describe_rates *r = response;
if (!p->clk->rate_discrete) {
switch (st->desc_index + st->loop_idx) {
case 0:
p->clk->range.min_rate = RATE_TO_U64(r->rate[0]);
break;
case 1:
p->clk->range.max_rate = RATE_TO_U64(r->rate[1]);
break;
case 2:
p->clk->range.step_size = RATE_TO_U64(r->rate[2]);
break;
default:
ret = -EINVAL;
break;
}
} else {
u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx];
p->clkd->rates[st->desc_index + st->loop_idx] =
RATE_TO_U64(r->rate[st->loop_idx]);
p->clkd->num_rates++;
*rate = RATE_TO_U64(r->rate[st->loop_idx]);
p->clk->list.num_rates++;
}
return ret;
return 0;
}
static int
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
struct scmi_clock_info *clk)
struct clock_info *cinfo)
{
int ret;
void *iter;
@ -519,9 +510,9 @@ 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 = {
.clk_id = clk_id,
.clk = clk,
.clkd = clkd,
.dev = ph->dev,
};
@ -536,16 +527,31 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
if (ret)
return ret;
if (!clk->rate_discrete) {
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
clk->range.min_rate, clk->range.max_rate,
clk->range.step_size);
} else if (clk->list.num_rates) {
sort(clk->list.rates, clk->list.num_rates,
sizeof(clk->list.rates[0]), rate_cmp_func, NULL);
}
/* empty set ? */
if (!clkd->num_rates)
return 0;
return ret;
if (!clkd->rate_discrete) {
clkd->info.range.min_rate = clkd->rates[RATE_MIN];
clkd->info.range.max_rate = clkd->rates[RATE_MAX];
clkd->info.range.step_size = clkd->rates[RATE_STEP];
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 {
unsigned int i;
sort(clkd->rates, clkd->num_rates,
sizeof(clkd->rates[0]), rate_cmp_func, NULL);
clkd->info.list.num_rates = clkd->num_rates;
for (i = 0; i < clkd->num_rates; i++)
clkd->info.list.rates[i] = clkd->rates[i];
clkd->info.max_rate = clkd->rates[clkd->num_rates - 1];
}
clkd->info.min_rate = clkd->rates[RATE_MIN];
return 0;
}
static int
@ -630,6 +636,7 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
{
u64 fmin, fmax, ftmp;
struct scmi_clock_info *clk;
struct scmi_clock_desc *clkd;
struct clock_info *ci = ph->get_priv(ph);
if (!rate)
@ -639,15 +646,17 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
if (IS_ERR(clk))
return PTR_ERR(clk);
clkd = to_desc(clk);
/*
* If we can't figure out what rate it will be, so just return the
* rate back to the caller.
*/
if (clk->rate_discrete)
if (clkd->rate_discrete)
return 0;
fmin = clk->range.min_rate;
fmax = clk->range.max_rate;
fmin = clk->min_rate;
fmax = clk->max_rate;
if (*rate <= fmin) {
*rate = fmin;
return 0;
@ -657,10 +666,10 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
}
ftmp = *rate - fmin;
ftmp += clk->range.step_size - 1; /* to round up */
ftmp = div64_ul(ftmp, clk->range.step_size);
ftmp += clkd->rates[RATE_STEP] - 1; /* to round up */
ftmp = div64_ul(ftmp, clkd->rates[RATE_STEP]);
*rate = ftmp * clk->range.step_size + fmin;
*rate = ftmp * clkd->rates[RATE_STEP] + fmin;
return 0;
}
@ -1122,17 +1131,16 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
sizeof(*cinfo->clk), GFP_KERNEL);
if (!cinfo->clk)
cinfo->clkds = devm_kcalloc(ph->dev, cinfo->num_clocks,
sizeof(*cinfo->clkds), GFP_KERNEL);
if (!cinfo->clkds)
return -ENOMEM;
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
struct scmi_clock_info *clk = cinfo->clk + clkid;
cinfo->clkds[clkid].id = clkid;
ret = scmi_clock_attributes_get(ph, clkid, cinfo);
if (!ret)
scmi_clock_describe_rates_get(ph, clkid, clk);
scmi_clock_describe_rates_get(ph, clkid, cinfo);
}
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {

View File

@ -51,6 +51,8 @@ struct scmi_clock_info {
bool rate_ctrl_forbidden;
bool parent_ctrl_forbidden;
bool extended_config;
u64 min_rate;
u64 max_rate;
union {
struct {
int num_rates;