706 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016-2022 NVIDIA Corporation4 */5 6#include <linux/clk-provider.h>7#include <linux/device.h>8#include <linux/seq_buf.h>9#include <linux/slab.h>10 11#include <soc/tegra/bpmp.h>12#include <soc/tegra/bpmp-abi.h>13 14#define TEGRA_BPMP_DUMP_CLOCK_INFO 015 16#define TEGRA_BPMP_CLK_HAS_MUX BIT(0)17#define TEGRA_BPMP_CLK_HAS_SET_RATE BIT(1)18#define TEGRA_BPMP_CLK_IS_ROOT BIT(2)19 20struct tegra_bpmp_clk_info {21 unsigned int id;22 char name[MRQ_CLK_NAME_MAXLEN];23 unsigned int parents[MRQ_CLK_MAX_PARENTS];24 unsigned int num_parents;25 unsigned long flags;26};27 28struct tegra_bpmp_clk {29 struct clk_hw hw;30 31 struct tegra_bpmp *bpmp;32 unsigned int id;33 34 unsigned int num_parents;35 unsigned int *parents;36};37 38static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)39{40 return container_of(hw, struct tegra_bpmp_clk, hw);41}42 43struct tegra_bpmp_clk_message {44 unsigned int cmd;45 unsigned int id;46 47 struct {48 const void *data;49 size_t size;50 } tx;51 52 struct {53 void *data;54 size_t size;55 int ret;56 } rx;57};58 59static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,60 const struct tegra_bpmp_clk_message *clk)61{62 struct mrq_clk_request request;63 struct tegra_bpmp_message msg;64 void *req = &request;65 int err;66 67 memset(&request, 0, sizeof(request));68 request.cmd_and_id = (clk->cmd << 24) | clk->id;69 70 /*71 * The mrq_clk_request structure has an anonymous union at offset 472 * that contains all possible sub-command structures. Copy the data73 * to that union. Ideally we'd be able to refer to it by name, but74 * doing so would require changing the ABI header and increase the75 * maintenance burden.76 */77 memcpy(req + 4, clk->tx.data, clk->tx.size);78 79 memset(&msg, 0, sizeof(msg));80 msg.mrq = MRQ_CLK;81 msg.tx.data = &request;82 msg.tx.size = sizeof(request);83 msg.rx.data = clk->rx.data;84 msg.rx.size = clk->rx.size;85 86 err = tegra_bpmp_transfer(bpmp, &msg);87 if (err < 0)88 return err;89 else if (msg.rx.ret < 0)90 return -EINVAL;91 92 return 0;93}94 95static int tegra_bpmp_clk_prepare(struct clk_hw *hw)96{97 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);98 struct tegra_bpmp_clk_message msg;99 100 memset(&msg, 0, sizeof(msg));101 msg.cmd = CMD_CLK_ENABLE;102 msg.id = clk->id;103 104 return tegra_bpmp_clk_transfer(clk->bpmp, &msg);105}106 107static void tegra_bpmp_clk_unprepare(struct clk_hw *hw)108{109 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);110 struct tegra_bpmp_clk_message msg;111 int err;112 113 memset(&msg, 0, sizeof(msg));114 msg.cmd = CMD_CLK_DISABLE;115 msg.id = clk->id;116 117 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);118 if (err < 0)119 dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",120 clk_hw_get_name(hw), err);121}122 123static int tegra_bpmp_clk_is_prepared(struct clk_hw *hw)124{125 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);126 struct cmd_clk_is_enabled_response response;127 struct tegra_bpmp_clk_message msg;128 int err;129 130 memset(&msg, 0, sizeof(msg));131 msg.cmd = CMD_CLK_IS_ENABLED;132 msg.id = clk->id;133 msg.rx.data = &response;134 msg.rx.size = sizeof(response);135 136 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);137 if (err < 0)138 return err;139 140 return response.state;141}142 143static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,144 unsigned long parent_rate)145{146 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);147 struct cmd_clk_get_rate_response response;148 struct cmd_clk_get_rate_request request;149 struct tegra_bpmp_clk_message msg;150 int err;151 152 memset(&msg, 0, sizeof(msg));153 msg.cmd = CMD_CLK_GET_RATE;154 msg.id = clk->id;155 msg.tx.data = &request;156 msg.tx.size = sizeof(request);157 msg.rx.data = &response;158 msg.rx.size = sizeof(response);159 160 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);161 if (err < 0)162 return 0;163 164 return response.rate;165}166 167static int tegra_bpmp_clk_determine_rate(struct clk_hw *hw,168 struct clk_rate_request *rate_req)169{170 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);171 struct cmd_clk_round_rate_response response;172 struct cmd_clk_round_rate_request request;173 struct tegra_bpmp_clk_message msg;174 unsigned long rate;175 int err;176 177 rate = min(max(rate_req->rate, rate_req->min_rate), rate_req->max_rate);178 179 memset(&request, 0, sizeof(request));180 request.rate = min_t(u64, rate, S64_MAX);181 182 memset(&msg, 0, sizeof(msg));183 msg.cmd = CMD_CLK_ROUND_RATE;184 msg.id = clk->id;185 msg.tx.data = &request;186 msg.tx.size = sizeof(request);187 msg.rx.data = &response;188 msg.rx.size = sizeof(response);189 190 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);191 if (err < 0)192 return err;193 194 rate_req->rate = (unsigned long)response.rate;195 196 return 0;197}198 199static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)200{201 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);202 struct cmd_clk_set_parent_response response;203 struct cmd_clk_set_parent_request request;204 struct tegra_bpmp_clk_message msg;205 int err;206 207 memset(&request, 0, sizeof(request));208 request.parent_id = clk->parents[index];209 210 memset(&msg, 0, sizeof(msg));211 msg.cmd = CMD_CLK_SET_PARENT;212 msg.id = clk->id;213 msg.tx.data = &request;214 msg.tx.size = sizeof(request);215 msg.rx.data = &response;216 msg.rx.size = sizeof(response);217 218 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);219 if (err < 0)220 return err;221 222 /* XXX check parent ID in response */223 224 return 0;225}226 227static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)228{229 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);230 struct cmd_clk_get_parent_response response;231 struct tegra_bpmp_clk_message msg;232 unsigned int i;233 int err;234 235 memset(&msg, 0, sizeof(msg));236 msg.cmd = CMD_CLK_GET_PARENT;237 msg.id = clk->id;238 msg.rx.data = &response;239 msg.rx.size = sizeof(response);240 241 err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);242 if (err < 0) {243 dev_err(clk->bpmp->dev, "failed to get parent for %s: %d\n",244 clk_hw_get_name(hw), err);245 return U8_MAX;246 }247 248 for (i = 0; i < clk->num_parents; i++)249 if (clk->parents[i] == response.parent_id)250 return i;251 252 return U8_MAX;253}254 255static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,256 unsigned long parent_rate)257{258 struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);259 struct cmd_clk_set_rate_response response;260 struct cmd_clk_set_rate_request request;261 struct tegra_bpmp_clk_message msg;262 263 memset(&request, 0, sizeof(request));264 request.rate = min_t(u64, rate, S64_MAX);265 266 memset(&msg, 0, sizeof(msg));267 msg.cmd = CMD_CLK_SET_RATE;268 msg.id = clk->id;269 msg.tx.data = &request;270 msg.tx.size = sizeof(request);271 msg.rx.data = &response;272 msg.rx.size = sizeof(response);273 274 return tegra_bpmp_clk_transfer(clk->bpmp, &msg);275}276 277static const struct clk_ops tegra_bpmp_clk_gate_ops = {278 .prepare = tegra_bpmp_clk_prepare,279 .unprepare = tegra_bpmp_clk_unprepare,280 .is_prepared = tegra_bpmp_clk_is_prepared,281 .recalc_rate = tegra_bpmp_clk_recalc_rate,282};283 284static const struct clk_ops tegra_bpmp_clk_mux_ops = {285 .prepare = tegra_bpmp_clk_prepare,286 .unprepare = tegra_bpmp_clk_unprepare,287 .is_prepared = tegra_bpmp_clk_is_prepared,288 .recalc_rate = tegra_bpmp_clk_recalc_rate,289 .determine_rate = clk_hw_determine_rate_no_reparent,290 .set_parent = tegra_bpmp_clk_set_parent,291 .get_parent = tegra_bpmp_clk_get_parent,292};293 294static const struct clk_ops tegra_bpmp_clk_rate_ops = {295 .prepare = tegra_bpmp_clk_prepare,296 .unprepare = tegra_bpmp_clk_unprepare,297 .is_prepared = tegra_bpmp_clk_is_prepared,298 .recalc_rate = tegra_bpmp_clk_recalc_rate,299 .determine_rate = tegra_bpmp_clk_determine_rate,300 .set_rate = tegra_bpmp_clk_set_rate,301};302 303static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {304 .prepare = tegra_bpmp_clk_prepare,305 .unprepare = tegra_bpmp_clk_unprepare,306 .is_prepared = tegra_bpmp_clk_is_prepared,307 .recalc_rate = tegra_bpmp_clk_recalc_rate,308 .determine_rate = tegra_bpmp_clk_determine_rate,309 .set_parent = tegra_bpmp_clk_set_parent,310 .get_parent = tegra_bpmp_clk_get_parent,311 .set_rate = tegra_bpmp_clk_set_rate,312};313 314static const struct clk_ops tegra_bpmp_clk_mux_read_only_ops = {315 .get_parent = tegra_bpmp_clk_get_parent,316 .recalc_rate = tegra_bpmp_clk_recalc_rate,317};318 319static const struct clk_ops tegra_bpmp_clk_read_only_ops = {320 .recalc_rate = tegra_bpmp_clk_recalc_rate,321};322 323static const struct clk_ops tegra_bpmp_clk_gate_mux_read_only_ops = {324 .prepare = tegra_bpmp_clk_prepare,325 .unprepare = tegra_bpmp_clk_unprepare,326 .is_prepared = tegra_bpmp_clk_is_prepared,327 .recalc_rate = tegra_bpmp_clk_recalc_rate,328 .get_parent = tegra_bpmp_clk_get_parent,329};330 331static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)332{333 struct cmd_clk_get_max_clk_id_response response;334 struct tegra_bpmp_clk_message msg;335 int err;336 337 memset(&msg, 0, sizeof(msg));338 msg.cmd = CMD_CLK_GET_MAX_CLK_ID;339 msg.rx.data = &response;340 msg.rx.size = sizeof(response);341 342 err = tegra_bpmp_clk_transfer(bpmp, &msg);343 if (err < 0)344 return err;345 346 if (response.max_id > INT_MAX)347 return -E2BIG;348 349 return response.max_id;350}351 352static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,353 struct tegra_bpmp_clk_info *info)354{355 struct cmd_clk_get_all_info_response response;356 struct tegra_bpmp_clk_message msg;357 unsigned int i;358 int err;359 360 memset(&msg, 0, sizeof(msg));361 msg.cmd = CMD_CLK_GET_ALL_INFO;362 msg.id = id;363 msg.rx.data = &response;364 msg.rx.size = sizeof(response);365 366 err = tegra_bpmp_clk_transfer(bpmp, &msg);367 if (err < 0)368 return err;369 370 strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);371 info->num_parents = response.num_parents;372 373 for (i = 0; i < info->num_parents; i++)374 info->parents[i] = response.parents[i];375 376 info->flags = response.flags;377 378 return 0;379}380 381static void tegra_bpmp_clk_info_dump(struct tegra_bpmp *bpmp,382 const char *level,383 const struct tegra_bpmp_clk_info *info)384{385 const char *prefix = "";386 struct seq_buf buf;387 unsigned int i;388 char flags[64];389 390 seq_buf_init(&buf, flags, sizeof(flags));391 392 if (info->flags)393 seq_buf_printf(&buf, "(");394 395 if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {396 seq_buf_printf(&buf, "%smux", prefix);397 prefix = ", ";398 }399 400 if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {401 seq_buf_printf(&buf, "%sfixed", prefix);402 prefix = ", ";403 }404 405 if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {406 seq_buf_printf(&buf, "%sroot", prefix);407 prefix = ", ";408 }409 410 if (info->flags)411 seq_buf_printf(&buf, ")");412 413 dev_printk(level, bpmp->dev, "%03u: %s\n", info->id, info->name);414 dev_printk(level, bpmp->dev, " flags: %lx %s\n", info->flags, flags);415 dev_printk(level, bpmp->dev, " parents: %u\n", info->num_parents);416 417 for (i = 0; i < info->num_parents; i++)418 dev_printk(level, bpmp->dev, " %03u\n", info->parents[i]);419}420 421static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,422 struct tegra_bpmp_clk_info **clocksp)423{424 struct tegra_bpmp_clk_info *clocks;425 unsigned int max_id, id, count = 0;426 unsigned int holes = 0;427 int err;428 429 err = tegra_bpmp_clk_get_max_id(bpmp);430 if (err < 0)431 return err;432 433 max_id = err;434 435 dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);436 437 clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);438 if (!clocks)439 return -ENOMEM;440 441 for (id = 0; id <= max_id; id++) {442 struct tegra_bpmp_clk_info *info = &clocks[count];443 444 err = tegra_bpmp_clk_get_info(bpmp, id, info);445 if (err < 0)446 continue;447 448 if (info->num_parents >= U8_MAX) {449 dev_err(bpmp->dev,450 "clock %u has too many parents (%u, max: %u)\n",451 id, info->num_parents, U8_MAX);452 continue;453 }454 455 /* clock not exposed by BPMP */456 if (info->name[0] == '\0') {457 holes++;458 continue;459 }460 461 info->id = id;462 count++;463 464 if (TEGRA_BPMP_DUMP_CLOCK_INFO)465 tegra_bpmp_clk_info_dump(bpmp, KERN_DEBUG, info);466 }467 468 dev_dbg(bpmp->dev, "holes: %u\n", holes);469 *clocksp = clocks;470 471 return count;472}473 474static unsigned int475tegra_bpmp_clk_id_to_index(const struct tegra_bpmp_clk_info *clocks,476 unsigned int num_clocks, unsigned int id)477{478 unsigned int i;479 480 for (i = 0; i < num_clocks; i++)481 if (clocks[i].id == id)482 return i;483 484 return UINT_MAX;485}486 487static const struct tegra_bpmp_clk_info *488tegra_bpmp_clk_find(const struct tegra_bpmp_clk_info *clocks,489 unsigned int num_clocks, unsigned int id)490{491 unsigned int i;492 493 i = tegra_bpmp_clk_id_to_index(clocks, num_clocks, id);494 495 if (i < num_clocks)496 return &clocks[i];497 498 return NULL;499}500 501static struct tegra_bpmp_clk *502tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,503 const struct tegra_bpmp_clk_info *info,504 const struct tegra_bpmp_clk_info *clocks,505 unsigned int num_clocks)506{507 struct tegra_bpmp_clk *clk;508 struct clk_init_data init;509 const char **parents;510 unsigned int i;511 int err;512 513 clk = devm_kzalloc(bpmp->dev, sizeof(*clk), GFP_KERNEL);514 if (!clk)515 return ERR_PTR(-ENOMEM);516 517 clk->id = info->id;518 clk->bpmp = bpmp;519 520 clk->parents = devm_kcalloc(bpmp->dev, info->num_parents,521 sizeof(*clk->parents), GFP_KERNEL);522 if (!clk->parents)523 return ERR_PTR(-ENOMEM);524 525 clk->num_parents = info->num_parents;526 527 /* hardware clock initialization */528 memset(&init, 0, sizeof(init));529 init.name = info->name;530 clk->hw.init = &init;531 if (info->flags & BPMP_CLK_STATE_CHANGE_DENIED) {532 if ((info->flags & BPMP_CLK_RATE_PARENT_CHANGE_DENIED) == 0) {533 dev_WARN(bpmp->dev,534 "Firmware bug! Inconsistent permission bits for clock %s. State and parent/rate changes disabled.",535 init.name);536 }537 if (info->flags & TEGRA_BPMP_CLK_HAS_MUX)538 init.ops = &tegra_bpmp_clk_mux_read_only_ops;539 else540 init.ops = &tegra_bpmp_clk_read_only_ops;541 } else if (info->flags & BPMP_CLK_RATE_PARENT_CHANGE_DENIED) {542 if (info->flags & TEGRA_BPMP_CLK_HAS_MUX)543 init.ops = &tegra_bpmp_clk_gate_mux_read_only_ops;544 else545 init.ops = &tegra_bpmp_clk_gate_ops;546 } else if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {547 if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)548 init.ops = &tegra_bpmp_clk_mux_rate_ops;549 else550 init.ops = &tegra_bpmp_clk_mux_ops;551 } else {552 if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)553 init.ops = &tegra_bpmp_clk_rate_ops;554 else555 init.ops = &tegra_bpmp_clk_gate_ops;556 }557 558 init.num_parents = info->num_parents;559 560 parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);561 if (!parents)562 return ERR_PTR(-ENOMEM);563 564 for (i = 0; i < info->num_parents; i++) {565 const struct tegra_bpmp_clk_info *parent;566 567 /* keep a private copy of the ID to parent index map */568 clk->parents[i] = info->parents[i];569 570 parent = tegra_bpmp_clk_find(clocks, num_clocks,571 info->parents[i]);572 if (!parent) {573 dev_err(bpmp->dev, "no parent %u found for %u\n",574 info->parents[i], info->id);575 continue;576 }577 578 parents[i] = parent->name;579 }580 581 init.parent_names = parents;582 583 err = devm_clk_hw_register(bpmp->dev, &clk->hw);584 585 kfree(parents);586 587 if (err < 0)588 return ERR_PTR(err);589 590 return clk;591}592 593static void tegra_bpmp_register_clocks_one(struct tegra_bpmp *bpmp,594 struct tegra_bpmp_clk_info *infos,595 unsigned int i,596 unsigned int count)597{598 unsigned int j;599 struct tegra_bpmp_clk_info *info;600 struct tegra_bpmp_clk *clk;601 602 if (bpmp->clocks[i]) {603 /* already registered */604 return;605 }606 607 info = &infos[i];608 for (j = 0; j < info->num_parents; ++j) {609 unsigned int p_id = info->parents[j];610 unsigned int p_i = tegra_bpmp_clk_id_to_index(infos, count,611 p_id);612 if (p_i < count)613 tegra_bpmp_register_clocks_one(bpmp, infos, p_i, count);614 }615 616 clk = tegra_bpmp_clk_register(bpmp, info, infos, count);617 if (IS_ERR(clk)) {618 dev_err(bpmp->dev,619 "failed to register clock %u (%s): %ld\n",620 info->id, info->name, PTR_ERR(clk));621 /* intentionally store the error pointer to622 * bpmp->clocks[i] to avoid re-attempting the623 * registration later624 */625 }626 627 bpmp->clocks[i] = clk;628}629 630static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,631 struct tegra_bpmp_clk_info *infos,632 unsigned int count)633{634 unsigned int i;635 636 bpmp->num_clocks = count;637 638 bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(struct tegra_bpmp_clk), GFP_KERNEL);639 if (!bpmp->clocks)640 return -ENOMEM;641 642 for (i = 0; i < count; i++) {643 tegra_bpmp_register_clocks_one(bpmp, infos, i, count);644 }645 646 return 0;647}648 649static void tegra_bpmp_unregister_clocks(struct tegra_bpmp *bpmp)650{651 unsigned int i;652 653 for (i = 0; i < bpmp->num_clocks; i++)654 clk_hw_unregister(&bpmp->clocks[i]->hw);655}656 657static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,658 void *data)659{660 unsigned int id = clkspec->args[0], i;661 struct tegra_bpmp *bpmp = data;662 663 for (i = 0; i < bpmp->num_clocks; i++) {664 struct tegra_bpmp_clk *clk = bpmp->clocks[i];665 666 if (!clk)667 continue;668 669 if (clk->id == id)670 return &clk->hw;671 }672 673 return NULL;674}675 676int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)677{678 struct tegra_bpmp_clk_info *clocks;679 unsigned int count;680 int err;681 682 err = tegra_bpmp_probe_clocks(bpmp, &clocks);683 if (err < 0)684 return err;685 686 count = err;687 688 dev_dbg(bpmp->dev, "%u clocks probed\n", count);689 690 err = tegra_bpmp_register_clocks(bpmp, clocks, count);691 if (err < 0)692 goto free;693 694 err = of_clk_add_hw_provider(bpmp->dev->of_node,695 tegra_bpmp_clk_of_xlate,696 bpmp);697 if (err < 0) {698 tegra_bpmp_unregister_clocks(bpmp);699 goto free;700 }701 702free:703 kfree(clocks);704 return err;705}706