Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.marussi@arm.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20231004-scmi-clock-v3-v5-1-1b8a1435673e@nxp.com
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

authored by

Peng Fan and committed by
Sudeep Holla
77bbfe60 3537a75e

+181 -6
+175 -6
drivers/firmware/arm_scmi/clock.c
··· 22 22 CLOCK_RATE_NOTIFY = 0x9, 23 23 CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA, 24 24 CLOCK_CONFIG_GET = 0xB, 25 + CLOCK_POSSIBLE_PARENTS_GET = 0xC, 26 + CLOCK_PARENT_SET = 0xD, 27 + CLOCK_PARENT_GET = 0xE, 25 28 }; 26 29 27 30 enum clk_state { ··· 45 42 #define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31)) 46 43 #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) 47 44 #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) 45 + #define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28)) 48 46 u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 49 47 __le32 clock_enable_latency; 48 + }; 49 + 50 + struct scmi_msg_clock_possible_parents { 51 + __le32 id; 52 + __le32 skip_parents; 53 + }; 54 + 55 + struct scmi_msg_resp_clock_possible_parents { 56 + __le32 num_parent_flags; 57 + #define NUM_PARENTS_RETURNED(x) ((x) & 0xff) 58 + #define NUM_PARENTS_REMAINING(x) ((x) >> 24) 59 + __le32 possible_parents[]; 60 + }; 61 + 62 + struct scmi_msg_clock_set_parent { 63 + __le32 id; 64 + __le32 parent_id; 50 65 }; 51 66 52 67 struct scmi_msg_clock_config_set { ··· 189 168 return ret; 190 169 } 191 170 171 + struct scmi_clk_ipriv { 172 + struct device *dev; 173 + u32 clk_id; 174 + struct scmi_clock_info *clk; 175 + }; 176 + 177 + static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index, 178 + const void *priv) 179 + { 180 + struct scmi_msg_clock_possible_parents *msg = message; 181 + const struct scmi_clk_ipriv *p = priv; 182 + 183 + msg->id = cpu_to_le32(p->clk_id); 184 + /* Set the number of OPPs to be skipped/already read */ 185 + msg->skip_parents = cpu_to_le32(desc_index); 186 + } 187 + 188 + static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st, 189 + const void *response, void *priv) 190 + { 191 + const struct scmi_msg_resp_clock_possible_parents *r = response; 192 + struct scmi_clk_ipriv *p = priv; 193 + struct device *dev = ((struct scmi_clk_ipriv *)p)->dev; 194 + u32 flags; 195 + 196 + flags = le32_to_cpu(r->num_parent_flags); 197 + st->num_returned = NUM_PARENTS_RETURNED(flags); 198 + st->num_remaining = NUM_PARENTS_REMAINING(flags); 199 + 200 + /* 201 + * num parents is not declared previously anywhere so we 202 + * assume it's returned+remaining on first call. 203 + */ 204 + if (!st->max_resources) { 205 + p->clk->num_parents = st->num_returned + st->num_remaining; 206 + p->clk->parents = devm_kcalloc(dev, p->clk->num_parents, 207 + sizeof(*p->clk->parents), 208 + GFP_KERNEL); 209 + if (!p->clk->parents) { 210 + p->clk->num_parents = 0; 211 + return -ENOMEM; 212 + } 213 + st->max_resources = st->num_returned + st->num_remaining; 214 + } 215 + 216 + return 0; 217 + } 218 + 219 + static int iter_clk_possible_parents_process_response(const struct scmi_protocol_handle *ph, 220 + const void *response, 221 + struct scmi_iterator_state *st, 222 + void *priv) 223 + { 224 + const struct scmi_msg_resp_clock_possible_parents *r = response; 225 + struct scmi_clk_ipriv *p = priv; 226 + 227 + u32 *parent = &p->clk->parents[st->desc_index + st->loop_idx]; 228 + 229 + *parent = le32_to_cpu(r->possible_parents[st->loop_idx]); 230 + 231 + return 0; 232 + } 233 + 234 + static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u32 clk_id, 235 + struct scmi_clock_info *clk) 236 + { 237 + struct scmi_iterator_ops ops = { 238 + .prepare_message = iter_clk_possible_parents_prepare_message, 239 + .update_state = iter_clk_possible_parents_update_state, 240 + .process_response = iter_clk_possible_parents_process_response, 241 + }; 242 + 243 + struct scmi_clk_ipriv ppriv = { 244 + .clk_id = clk_id, 245 + .clk = clk, 246 + .dev = ph->dev, 247 + }; 248 + void *iter; 249 + int ret; 250 + 251 + iter = ph->hops->iter_response_init(ph, &ops, 0, 252 + CLOCK_POSSIBLE_PARENTS_GET, 253 + sizeof(struct scmi_msg_clock_possible_parents), 254 + &ppriv); 255 + if (IS_ERR(iter)) 256 + return PTR_ERR(iter); 257 + 258 + ret = ph->hops->iter_response_run(iter); 259 + 260 + return ret; 261 + } 262 + 192 263 static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, 193 264 u32 clk_id, struct scmi_clock_info *clk, 194 265 u32 version) ··· 325 212 clk->rate_changed_notifications = true; 326 213 if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) 327 214 clk->rate_change_requested_notifications = true; 215 + if (SUPPORTS_PARENT_CLOCK(attributes)) 216 + scmi_clock_possible_parents(ph, clk_id, clk); 328 217 } 329 218 330 219 return ret; ··· 343 228 else 344 229 return 1; 345 230 } 346 - 347 - struct scmi_clk_ipriv { 348 - struct device *dev; 349 - u32 clk_id; 350 - struct scmi_clock_info *clk; 351 - }; 352 231 353 232 static void iter_clk_describe_prepare_message(void *message, 354 233 const unsigned int desc_index, ··· 567 458 return ret; 568 459 } 569 460 461 + static int 462 + scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id, 463 + u32 parent_id) 464 + { 465 + int ret; 466 + struct scmi_xfer *t; 467 + struct scmi_msg_clock_set_parent *cfg; 468 + struct clock_info *ci = ph->get_priv(ph); 469 + struct scmi_clock_info *clk; 470 + 471 + if (clk_id >= ci->num_clocks) 472 + return -EINVAL; 473 + 474 + clk = ci->clk + clk_id; 475 + 476 + if (parent_id >= clk->num_parents) 477 + return -EINVAL; 478 + 479 + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET, 480 + sizeof(*cfg), 0, &t); 481 + if (ret) 482 + return ret; 483 + 484 + t->hdr.poll_completion = false; 485 + 486 + cfg = t->tx.buf; 487 + cfg->id = cpu_to_le32(clk_id); 488 + cfg->parent_id = cpu_to_le32(clk->parents[parent_id]); 489 + 490 + ret = ph->xops->do_xfer(ph, t); 491 + 492 + ph->xops->xfer_put(ph, t); 493 + 494 + return ret; 495 + } 496 + 497 + static int 498 + scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id, 499 + u32 *parent_id) 500 + { 501 + int ret; 502 + struct scmi_xfer *t; 503 + 504 + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_GET, 505 + sizeof(__le32), sizeof(u32), &t); 506 + if (ret) 507 + return ret; 508 + 509 + put_unaligned_le32(clk_id, t->tx.buf); 510 + 511 + ret = ph->xops->do_xfer(ph, t); 512 + if (!ret) 513 + *parent_id = get_unaligned_le32(t->rx.buf); 514 + 515 + ph->xops->xfer_put(ph, t); 516 + return ret; 517 + } 518 + 570 519 /* For SCMI clock v2.1 and onwards */ 571 520 static int 572 521 scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, ··· 817 650 .state_get = scmi_clock_state_get, 818 651 .config_oem_get = scmi_clock_config_oem_get, 819 652 .config_oem_set = scmi_clock_config_oem_set, 653 + .parent_set = scmi_clock_set_parent, 654 + .parent_get = scmi_clock_get_parent, 820 655 }; 821 656 822 657 static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
+6
include/linux/scmi_protocol.h
··· 58 58 u64 step_size; 59 59 } range; 60 60 }; 61 + int num_parents; 62 + u32 *parents; 61 63 }; 62 64 63 65 enum scmi_power_scale { ··· 85 83 * @state_get: get the status of the specified clock 86 84 * @config_oem_get: get the value of an OEM specific clock config 87 85 * @config_oem_set: set the value of an OEM specific clock config 86 + * @parent_get: get the parent id of a clk 87 + * @parent_set: set the parent of a clock 88 88 */ 89 89 struct scmi_clk_proto_ops { 90 90 int (*count_get)(const struct scmi_protocol_handle *ph); ··· 108 104 bool atomic); 109 105 int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id, 110 106 u8 oem_type, u32 oem_val, bool atomic); 107 + int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id); 108 + int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id); 111 109 }; 112 110 113 111 struct scmi_perf_domain_info {