Based on Linux commit: | commit 77bbfe607b1d306c88bf96fed00c030f6bf462f1 | Author: Peng Fan <peng....@nxp.com> | Date: Wed Oct 4 07:42:23 2023 +0800 | | firmware: arm_scmi: Add support for clock parents | | SCMI v3.2 spec introduces CLOCK_POSSIBLE_PARENTS_GET, CLOCK_PARENT_SET | and CLOCK_PARENT_GET. Add support for these to enable clock parents | and use them in the clock driver. | | Reviewed-by: Cristian Marussi <cristian.maru...@arm.com> | Signed-off-by: Peng Fan <peng....@nxp.com> | Link: https://lore.kernel.org/r/20231004-scmi-clock-v3-v5-1-1b8a14356...@nxp.com | Signed-off-by: Sudeep Holla <sudeep.ho...@arm.com>
Signed-off-by: Sascha Hauer <s.ha...@pengutronix.de> --- drivers/firmware/arm_scmi/clock.c | 179 ++++++++++++++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 6 ++ 2 files changed, 179 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 2c902835a0ecbfdf5513fd188f7f2b84ddb65cfc..6b8ef9321db6f799f2c47c3748fe64e0781f6396 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -17,6 +17,9 @@ enum scmi_clock_protocol_cmd { CLOCK_RATE_GET = 0x6, CLOCK_CONFIG_SET = 0x7, CLOCK_NAME_GET = 0x8, + CLOCK_POSSIBLE_PARENTS_GET = 0xC, + CLOCK_PARENT_SET = 0xD, + CLOCK_PARENT_GET = 0xE, }; struct scmi_msg_resp_clock_protocol_attributes { @@ -29,10 +32,28 @@ struct scmi_msg_resp_clock_attributes { __le32 attributes; #define CLOCK_ENABLE BIT(0) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) +#define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; }; +struct scmi_msg_clock_possible_parents { + __le32 id; + __le32 skip_parents; +}; + +struct scmi_msg_resp_clock_possible_parents { + __le32 num_parent_flags; +#define NUM_PARENTS_RETURNED(x) ((x) & 0xff) +#define NUM_PARENTS_REMAINING(x) ((x) >> 24) + __le32 possible_parents[]; +}; + +struct scmi_msg_clock_set_parent { + __le32 id; + __le32 parent_id; +}; + struct scmi_clock_set_config { __le32 id; __le32 attributes; @@ -105,6 +126,98 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, return ret; } +struct scmi_clk_ipriv { + struct device *dev; + u32 clk_id; + struct scmi_clock_info *clk; +}; + +static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_clock_possible_parents *msg = message; + const struct scmi_clk_ipriv *p = priv; + + msg->id = cpu_to_le32(p->clk_id); + /* Set the number of OPPs to be skipped/already read */ + msg->skip_parents = cpu_to_le32(desc_index); +} + +static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + 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); + st->num_returned = NUM_PARENTS_RETURNED(flags); + st->num_remaining = NUM_PARENTS_REMAINING(flags); + + /* + * num parents is not declared previously anywhere so we + * 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; + return -ENOMEM; + } + st->max_resources = st->num_returned + st->num_remaining; + } + + return 0; +} + +static int iter_clk_possible_parents_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, + void *priv) +{ + 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]; + + *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) +{ + 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_clk_ipriv ppriv = { + .clk_id = clk_id, + .clk = clk, + .dev = ph->dev, + }; + void *iter; + int ret; + + iter = ph->hops->iter_response_init(ph, &ops, 0, + CLOCK_POSSIBLE_PARENTS_GET, + sizeof(struct scmi_msg_clock_possible_parents), + &ppriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret = ph->hops->iter_response_run(iter); + + return ret; +} + static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, u32 clk_id, struct scmi_clock_info *clk, u32 version) @@ -145,6 +258,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, clk->name, SCMI_MAX_STR_SIZE); + if (SUPPORTS_PARENT_CLOCK(attributes)) + scmi_clock_possible_parents(ph, clk_id, clk); } return ret; @@ -162,12 +277,6 @@ static int rate_cmp_func(const void *_r1, const void *_r2) return 1; } -struct scmi_clk_ipriv { - struct device *dev; - u32 clk_id; - struct scmi_clock_info *clk; -}; - static void iter_clk_describe_prepare_message(void *message, const unsigned int desc_index, const void *priv) @@ -406,6 +515,62 @@ scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id) return clk; } +static int +scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id, + u32 parent_id) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_clock_set_parent *cfg; + struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + if (clk_id >= ci->num_clocks) + return -EINVAL; + + clk = ci->clk + clk_id; + + if (parent_id >= clk->num_parents) + return -EINVAL; + + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET, + sizeof(*cfg), 0, &t); + if (ret) + return ret; + + cfg = t->tx.buf; + cfg->id = cpu_to_le32(clk_id); + cfg->parent_id = cpu_to_le32(clk->parents[parent_id]); + + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int +scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id, + u32 *parent_id) +{ + int ret; + struct scmi_xfer *t; + + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_GET, + sizeof(__le32), sizeof(u32), &t); + if (ret) + return ret; + + put_unaligned_le32(clk_id, t->tx.buf); + + ret = ph->xops->do_xfer(ph, t); + if (!ret) + *parent_id = get_unaligned_le32(t->rx.buf); + + ph->xops->xfer_put(ph, t); + return ret; +} + static const struct scmi_clk_proto_ops clk_proto_ops = { .count_get = scmi_clock_count_get, .info_get = scmi_clock_info_get, @@ -415,6 +580,8 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .disable = scmi_clock_disable, .enable_atomic = scmi_clock_enable_atomic, .disable_atomic = scmi_clock_disable_atomic, + .parent_set = scmi_clock_set_parent, + .parent_get = scmi_clock_get_parent, }; static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index a33cb497a12b0e08209b2a1d22e319c7849ced2e..c9cacb7f617a90b6f280c60787eb010a6edbefbc 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -59,6 +59,8 @@ struct scmi_clock_info { u64 step_size; } range; }; + int num_parents; + u32 *parents; }; enum scmi_power_scale { @@ -81,6 +83,8 @@ struct scmi_protocol_handle; * @rate_set: set the clock rate of a clock * @enable: enables the specified clock * @disable: disables the specified clock + * @parent_get: get the parent id of a clk + * @parent_set: set the parent of a clock */ struct scmi_clk_proto_ops { int (*count_get)(const struct scmi_protocol_handle *ph); @@ -96,6 +100,8 @@ struct scmi_clk_proto_ops { int (*enable_atomic)(const struct scmi_protocol_handle *ph, u32 clk_id); int (*disable_atomic)(const struct scmi_protocol_handle *ph, u32 clk_id); + int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id); + int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id); }; /** -- 2.39.5