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