From dc7ccfc86c1af6406c5a0aeeac122e64711ea5da Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 5 Mar 2026 09:56:44 +0800 Subject: firmware: arm_scmi: imx: Support getting reset reason of MISC protocol MISC protocol supports getting reset reason per Logical Machine or System. Add the API for user to retrieve the information from System Manager. Signed-off-by: Peng Fan Reviewed-by: Daniel Baluta Link: https://patch.msgid.link/20260305-scmi-imx-reset-v1-1-18de78978ba9@nxp.com Signed-off-by: Sudeep Holla --- include/linux/scmi_imx_protocol.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include/linux') diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h index 2407d7693b6b..ab867463c08c 100644 --- a/include/linux/scmi_imx_protocol.h +++ b/include/linux/scmi_imx_protocol.h @@ -52,6 +52,17 @@ struct scmi_imx_misc_ctrl_notify_report { unsigned int flags; }; + +#define MISC_EXT_INFO_LEN_MAX 4 +struct scmi_imx_misc_reset_reason { + bool valid:1; + bool orig_valid:1; + bool err_valid:1; + u32 reason; + u32 origin; + u32 errid; +}; + struct scmi_imx_misc_proto_ops { int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32 id, u32 num, u32 *val); @@ -61,6 +72,9 @@ struct scmi_imx_misc_proto_ops { u32 ctrl_id, u32 evt_id, u32 flags); int (*misc_syslog)(const struct scmi_protocol_handle *ph, u16 *size, void *array); + int (*misc_reset_reason)(const struct scmi_protocol_handle *ph, + bool system, struct scmi_imx_misc_reset_reason *boot_r, + struct scmi_imx_misc_reset_reason *shut_r, u32 *extinfo); }; /* See LMM_ATTRIBUTES in imx95.rst */ -- cgit v1.2.3 From 0c6eb5d019c1e4b9cfcfc47000e08858bbbc5e52 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 6 Apr 2026 17:52:54 +0200 Subject: firmware: arm_scmi: Rename struct scmi_revision_info to scmi_base_info Rename struct scmi_revision_info to struct scmi_base_info , to accurately represent its content. The scmi_revision_info is no longer accurate, because the structure now contains more than only SCMI base protocol revision, it now also contains number of protocols, agents, vendor and subvendor strings. All those are fetched from the base protocol, so rename the structure to scmi_base_info, to match the other scmi_*_info structure names. No functional change. Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20260406155343.72087-1-marek.vasut+renesas@mailbox.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 10 +++++----- drivers/firmware/arm_scmi/common.h | 2 +- drivers/firmware/arm_scmi/driver.c | 14 +++++++------- include/linux/scmi_protocol.h | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index cd1331c2fc40..4df2620e3c5d 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -69,7 +69,7 @@ static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph) int ret; struct scmi_xfer *t; struct scmi_msg_resp_base_attributes *attr_info; - struct scmi_revision_info *rev = ph->get_priv(ph); + struct scmi_base_info *rev = ph->get_priv(ph); ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, sizeof(*attr_info), &t); @@ -103,7 +103,7 @@ scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor) int ret, size; char *vendor_id; struct scmi_xfer *t; - struct scmi_revision_info *rev = ph->get_priv(ph); + struct scmi_base_info *rev = ph->get_priv(ph); if (sub_vendor) { cmd = BASE_DISCOVER_SUB_VENDOR; @@ -143,7 +143,7 @@ scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph) int ret; __le32 *impl_ver; struct scmi_xfer *t; - struct scmi_revision_info *rev = ph->get_priv(ph); + struct scmi_base_info *rev = ph->get_priv(ph); ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION, 0, sizeof(*impl_ver), &t); @@ -180,7 +180,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, __le32 *num_skip, *num_ret; u32 tot_num_ret = 0, loop_num_ret; struct device *dev = ph->dev; - struct scmi_revision_info *rev = ph->get_priv(ph); + struct scmi_base_info *rev = ph->get_priv(ph); ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS, sizeof(*num_skip), 0, &t); @@ -377,7 +377,7 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) u8 *prot_imp; char name[SCMI_SHORT_NAME_MAX_SIZE]; struct device *dev = ph->dev; - struct scmi_revision_info *rev = scmi_revision_area_get(ph); + struct scmi_base_info *rev = scmi_revision_area_get(ph); rev->major_ver = PROTOCOL_REV_MAJOR(ph->version); rev->minor_ver = PROTOCOL_REV_MINOR(ph->version); diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 7c9617d080a0..07a127dec031 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -138,7 +138,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) xfer_; \ }) -struct scmi_revision_info * +struct scmi_base_info * scmi_revision_area_get(const struct scmi_protocol_handle *ph); void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, u8 *prot_imp); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index f167194f7cf6..f7ee1b1495d7 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -133,7 +133,7 @@ struct scmi_protocol_instance { * usage. * @protocols_mtx: A mutex to protect protocols instances initialization. * @protocols_imp: List of protocols implemented, currently maximum of - * scmi_revision_info.num_protocols elements allocated by the + * scmi_base_info.num_protocols elements allocated by the * base protocol * @active_protocols: IDR storing device_nodes for protocols actually defined * in the DT and confirmed as implemented by fw. @@ -151,7 +151,7 @@ struct scmi_info { int id; struct device *dev; const struct scmi_desc *desc; - struct scmi_revision_info version; + struct scmi_base_info version; struct scmi_handle handle; struct scmi_xfers_info tx_minfo; struct scmi_xfers_info rx_minfo; @@ -265,7 +265,7 @@ scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id, } static const struct scmi_protocol * -scmi_vendor_protocol_get(int protocol_id, struct scmi_revision_info *version) +scmi_vendor_protocol_get(int protocol_id, struct scmi_base_info *version) { const struct scmi_protocol *proto; @@ -303,7 +303,7 @@ scmi_vendor_protocol_get(int protocol_id, struct scmi_revision_info *version) } static const struct scmi_protocol * -scmi_protocol_get(int protocol_id, struct scmi_revision_info *version) +scmi_protocol_get(int protocol_id, struct scmi_base_info *version) { const struct scmi_protocol *proto = NULL; @@ -2063,7 +2063,7 @@ static const struct scmi_proto_helpers_ops helpers_ops = { * Return: A reference to the version memory area associated to the SCMI * instance underlying this protocol handle. */ -struct scmi_revision_info * +struct scmi_base_info * scmi_revision_area_get(const struct scmi_protocol_handle *ph) { const struct scmi_protocol_instance *pi = ph_to_pi(ph); @@ -2376,7 +2376,7 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id) { int i; struct scmi_info *info = handle_to_scmi_info(handle); - struct scmi_revision_info *rev = handle->version; + struct scmi_base_info *rev = handle->version; if (!info->protocols_imp) return false; @@ -3171,7 +3171,7 @@ static const struct scmi_desc *scmi_transport_setup(struct device *dev) static void scmi_enable_matching_quirks(struct scmi_info *info) { - struct scmi_revision_info *rev = &info->version; + struct scmi_base_info *rev = &info->version; dev_dbg(info->dev, "Looking for quirks matching: %s/%s/0x%08X\n", rev->vendor_id, rev->sub_vendor_id, rev->impl_ver); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index aafaac1496b0..8d0b106caed8 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -18,7 +18,7 @@ #define SCMI_MAX_NUM_RATES 16 /** - * struct scmi_revision_info - version information structure + * struct scmi_base_info - version information structure * * @major_ver: Major ABI version. Change here implies risk of backward * compatibility break. @@ -31,7 +31,7 @@ * @vendor_id: A vendor identifier(Null terminated ASCII string) * @sub_vendor_id: A sub-vendor identifier(Null terminated ASCII string) */ -struct scmi_revision_info { +struct scmi_base_info { u16 major_ver; u16 minor_ver; u8 num_protocols; @@ -901,7 +901,7 @@ struct scmi_notify_ops { */ struct scmi_handle { struct device *dev; - struct scmi_revision_info *version; + struct scmi_base_info *version; int __must_check (*devm_protocol_acquire)(struct scmi_device *sdev, u8 proto); -- cgit v1.2.3 From ecde921eb46022acbdbfff2ad4e4c6e6d0493430 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:47 +0100 Subject: firmware: arm_scmi: Add clock determine_rate operation Add a clock operation to help determining the effective rate, closest to the required one, that a specific clock can support. Calculation is currently performed kernel side and the logic is taken directly from the SCMI Clock driver: embedding the determinate rate logic in the protocol layer enables simplifications in the SCMI Clock protocol interface and will more easily accommodate further evolutions where such determine_rate logic into is optionally delegated to the platform SCMI server. Signed-off-by: Cristian Marussi Tested-by: Florian Fainelli Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260508153300.2224715-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 42 +++++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 6 ++++++ 2 files changed, 48 insertions(+) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index ab36871650a1..54b55517b759 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -5,6 +5,7 @@ * Copyright (C) 2018-2022 ARM Ltd. */ +#include #include #include #include @@ -624,6 +625,46 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, return ret; } +static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, + u32 clk_id, unsigned long *rate) +{ + u64 fmin, fmax, ftmp; + struct scmi_clock_info *clk; + struct clock_info *ci = ph->get_priv(ph); + + if (!rate) + return -EINVAL; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(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) + return 0; + + fmin = clk->range.min_rate; + fmax = clk->range.max_rate; + if (*rate <= fmin) { + *rate = fmin; + return 0; + } else if (*rate >= fmax) { + *rate = fmax; + return 0; + } + + ftmp = *rate - fmin; + ftmp += clk->range.step_size - 1; /* to round up */ + ftmp = div64_ul(ftmp, clk->range.step_size); + + *rate = ftmp * clk->range.step_size + fmin; + + return 0; +} + static int scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, enum clk_state state, @@ -936,6 +977,7 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .info_get = scmi_clock_info_get, .rate_get = scmi_clock_rate_get, .rate_set = scmi_clock_rate_set, + .determine_rate = scmi_clock_determine_rate, .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 8d0b106caed8..984117f51695 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -91,6 +91,10 @@ enum scmi_clock_oem_config { * @info_get: get the information of the specified clock * @rate_get: request the current clock rate of a clock * @rate_set: set the clock rate of a clock + * @determine_rate: determine the effective rate that can be supported by a + * 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 * @enable: enables the specified clock * @disable: disables the specified clock * @state_get: get the status of the specified clock @@ -108,6 +112,8 @@ struct scmi_clk_proto_ops { u64 *rate); int (*rate_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u64 rate); + int (*determine_rate)(const struct scmi_protocol_handle *ph, u32 clk_id, + unsigned long *rate); int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic); int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id, -- cgit v1.2.3 From 0d76f62613cafecb7d326a5a45619024fa7e6e8e Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:49 +0100 Subject: 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 Signed-off-by: Cristian Marussi Tested-by: Florian Fainelli Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260508153300.2224715-5-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 154 ++++++++++++++++++++------------------ include/linux/scmi_protocol.h | 2 + 2 files changed, 83 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 54b55517b759..512400122b85 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -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) { + /* empty set ? */ + if (!clkd->num_rates) + return 0; + + 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", - 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); + 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 ret; + 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) { diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 984117f51695..f82747adc8eb 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -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; -- cgit v1.2.3 From 2e757f71a5ab861478204e2907bb373ccb3ca087 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:51 +0100 Subject: firmware: arm_scmi: Drop unused clock rate interfaces Only the unified interface exposing min_rate/max_rate is now used. Reviewed-by: Peng Fan Signed-off-by: Cristian Marussi Link: https://patch.msgid.link/20260508153300.2224715-7-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 9 --------- include/linux/scmi_protocol.h | 12 ------------ 2 files changed, 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 512400122b85..467b13a3a18f 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -467,7 +467,6 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st, st->num_remaining = NUM_REMAINING(flags); st->num_returned = NUM_RETURNED(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->clkd->rate_discrete && @@ -532,21 +531,13 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id, return 0; 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]; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index f82747adc8eb..9913b4f097d8 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -44,7 +44,6 @@ struct scmi_base_info { struct scmi_clock_info { char name[SCMI_MAX_STR_SIZE]; unsigned int enable_latency; - bool rate_discrete; bool rate_changed_notifications; bool rate_change_requested_notifications; bool state_ctrl_forbidden; @@ -53,17 +52,6 @@ struct scmi_clock_info { bool extended_config; u64 min_rate; u64 max_rate; - union { - struct { - int num_rates; - u64 rates[SCMI_MAX_NUM_RATES]; - } list; - struct { - u64 min_rate; - u64 max_rate; - u64 step_size; - } range; - }; int num_parents; u32 *parents; }; -- cgit v1.2.3 From 62ba967595e0b2599768f886851ba5e0d4bfb55b Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:52 +0100 Subject: firmware: arm_scmi: Make clock rates allocation dynamic Leveraging SCMI Clock protocol dynamic discovery capabilities, move away from the static per-clock rates allocation model in favour of a dynamic runtime allocation based on effectively discovered resources. No functional change. Reviewed-by: Peng Fan Signed-off-by: Cristian Marussi Tested-by: Florian Fainelli Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260508153300.2224715-8-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 19 ++++++++++++++++--- include/linux/scmi_protocol.h | 1 - 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 467b13a3a18f..c9b62edce4fd 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -161,7 +161,7 @@ struct scmi_clock_desc { u32 id; bool rate_discrete; unsigned int num_rates; - u64 rates[SCMI_MAX_NUM_RATES]; + u64 *rates; #define RATE_MIN 0 #define RATE_MAX 1 #define RATE_STEP 2 @@ -480,6 +480,18 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st, QUIRK_OUT_OF_SPEC_TRIPLET); } + if (!st->max_resources) { + int num_rates = st->num_returned + st->num_remaining; + + p->clkd->rates = devm_kcalloc(p->dev, num_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; + } + return 0; } @@ -493,6 +505,8 @@ iter_clk_describe_process_response(const struct scmi_protocol_handle *ph, p->clkd->rates[st->desc_index + st->loop_idx] = RATE_TO_U64(r->rate[st->loop_idx]); + + /* Count only effectively discovered rates */ p->clkd->num_rates++; return 0; @@ -515,8 +529,7 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id, .dev = ph->dev, }; - iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES, - CLOCK_DESCRIBE_RATES, + iter = ph->hops->iter_response_init(ph, &ops, 0, CLOCK_DESCRIBE_RATES, sizeof(struct scmi_msg_clock_describe_rates), &cpriv); if (IS_ERR(iter)) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9913b4f097d8..55db9ba8fac3 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -15,7 +15,6 @@ #define SCMI_MAX_STR_SIZE 64 #define SCMI_SHORT_NAME_MAX_SIZE 16 -#define SCMI_MAX_NUM_RATES 16 /** * struct scmi_base_info - version information structure -- cgit v1.2.3 From d2488ff1a257342111e1be1348d52e8b4ecfaa36 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:33:00 +0100 Subject: firmware: arm_scmi: Introduce all_rates_get clock operation 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. Reviewed-by: Peng Fan Signed-off-by: Cristian Marussi Link: https://patch.msgid.link/20260508153300.2224715-16-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 83 +++++++++++++++++++++++++++------------ include/linux/scmi_protocol.h | 9 +++++ 2 files changed, 66 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index ab8c65ed785a..42e666a628c7 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; } @@ -586,7 +590,7 @@ scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph, * If discrete and we don't already have it, grab the last value, which * should be the max */ - if (clkd->rate_discrete && clkd->tot_rates > clkd->num_rates) { + if (clkd->r.rate_discrete && clkd->tot_rates > clkd->r.num_rates) { first = clkd->tot_rates - 1; last = clkd->tot_rates - 1; ret = ph->hops->iter_response_run_bound(iter, &first, &last); @@ -618,14 +622,14 @@ 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 Num_Rates:%u -> Min %llu Max %llu\n", clkd->info.name, clkd->tot_rates, clkd->info.min_rate, clkd->info.max_rate); @@ -732,7 +736,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; @@ -746,14 +750,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 */ - ftmp = div64_ul(ftmp, clkd->rates[RATE_STEP]); + ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */ + ftmp = div64_ul(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, @@ -1067,6 +1097,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 55db9ba8fac3..5ab73b1ab9aa 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -40,6 +40,12 @@ struct scmi_base_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; @@ -84,6 +90,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 @@ -103,6 +110,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, -- cgit v1.2.3