linux/drivers/clk/clk-axm5516.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * drivers/clk/clk-axm5516.c
   4 *
   5 * Provides clock implementations for three different types of clock devices on
   6 * the Axxia device: PLL clock, a clock divider and a clock mux.
   7 *
   8 * Copyright (C) 2014 LSI Corporation
   9 */
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/slab.h>
  13#include <linux/platform_device.h>
  14#include <linux/of.h>
  15#include <linux/of_address.h>
  16#include <linux/clk-provider.h>
  17#include <linux/regmap.h>
  18#include <dt-bindings/clock/lsi,axm5516-clks.h>
  19
  20
  21/**
  22 * struct axxia_clk - Common struct to all Axxia clocks.
  23 * @hw: clk_hw for the common clk framework
  24 * @regmap: Regmap for the clock control registers
  25 */
  26struct axxia_clk {
  27        struct clk_hw hw;
  28        struct regmap *regmap;
  29};
  30#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
  31
  32/**
  33 * struct axxia_pllclk - Axxia PLL generated clock.
  34 * @aclk: Common struct
  35 * @reg: Offset into regmap for PLL control register
  36 */
  37struct axxia_pllclk {
  38        struct axxia_clk aclk;
  39        u32 reg;
  40};
  41#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
  42
  43/**
  44 * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
  45 * parent clock rate.
  46 */
  47static unsigned long
  48axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
  49{
  50        struct axxia_clk *aclk = to_axxia_clk(hw);
  51        struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
  52        unsigned long rate, fbdiv, refdiv, postdiv;
  53        u32 control;
  54
  55        regmap_read(aclk->regmap, pll->reg, &control);
  56        postdiv = ((control >> 0) & 0xf) + 1;
  57        fbdiv   = ((control >> 4) & 0xfff) + 3;
  58        refdiv  = ((control >> 16) & 0x1f) + 1;
  59        rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
  60
  61        return rate;
  62}
  63
  64static const struct clk_ops axxia_pllclk_ops = {
  65        .recalc_rate = axxia_pllclk_recalc,
  66};
  67
  68/**
  69 * struct axxia_divclk - Axxia clock divider
  70 * @aclk: Common struct
  71 * @reg: Offset into regmap for PLL control register
  72 * @shift: Bit position for divider value
  73 * @width: Number of bits in divider value
  74 */
  75struct axxia_divclk {
  76        struct axxia_clk aclk;
  77        u32 reg;
  78        u32 shift;
  79        u32 width;
  80};
  81#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
  82
  83/**
  84 * axxia_divclk_recalc_rate - Calculate clock divider output rage
  85 */
  86static unsigned long
  87axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
  88{
  89        struct axxia_clk *aclk = to_axxia_clk(hw);
  90        struct axxia_divclk *divclk = to_axxia_divclk(aclk);
  91        u32 ctrl, div;
  92
  93        regmap_read(aclk->regmap, divclk->reg, &ctrl);
  94        div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
  95
  96        return parent_rate / div;
  97}
  98
  99static const struct clk_ops axxia_divclk_ops = {
 100        .recalc_rate = axxia_divclk_recalc_rate,
 101};
 102
 103/**
 104 * struct axxia_clkmux - Axxia clock mux
 105 * @aclk: Common struct
 106 * @reg: Offset into regmap for PLL control register
 107 * @shift: Bit position for selection value
 108 * @width: Number of bits in selection value
 109 */
 110struct axxia_clkmux {
 111        struct axxia_clk aclk;
 112        u32 reg;
 113        u32 shift;
 114        u32 width;
 115};
 116#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
 117
 118/**
 119 * axxia_clkmux_get_parent - Return the index of selected parent clock
 120 */
 121static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
 122{
 123        struct axxia_clk *aclk = to_axxia_clk(hw);
 124        struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
 125        u32 ctrl, parent;
 126
 127        regmap_read(aclk->regmap, mux->reg, &ctrl);
 128        parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
 129
 130        return (u8) parent;
 131}
 132
 133static const struct clk_ops axxia_clkmux_ops = {
 134        .get_parent = axxia_clkmux_get_parent,
 135};
 136
 137
 138/*
 139 * PLLs
 140 */
 141
 142static struct axxia_pllclk clk_fab_pll = {
 143        .aclk.hw.init = &(struct clk_init_data){
 144                .name = "clk_fab_pll",
 145                .parent_names = (const char *[]){
 146                        "clk_ref0"
 147                },
 148                .num_parents = 1,
 149                .ops = &axxia_pllclk_ops,
 150        },
 151        .reg   = 0x01800,
 152};
 153
 154static struct axxia_pllclk clk_cpu_pll = {
 155        .aclk.hw.init = &(struct clk_init_data){
 156                .name = "clk_cpu_pll",
 157                .parent_names = (const char *[]){
 158                        "clk_ref0"
 159                },
 160                .num_parents = 1,
 161                .ops = &axxia_pllclk_ops,
 162        },
 163        .reg   = 0x02000,
 164};
 165
 166static struct axxia_pllclk clk_sys_pll = {
 167        .aclk.hw.init = &(struct clk_init_data){
 168                .name = "clk_sys_pll",
 169                .parent_names = (const char *[]){
 170                        "clk_ref0"
 171                },
 172                .num_parents = 1,
 173                .ops = &axxia_pllclk_ops,
 174        },
 175        .reg   = 0x02800,
 176};
 177
 178static struct axxia_pllclk clk_sm0_pll = {
 179        .aclk.hw.init = &(struct clk_init_data){
 180                .name = "clk_sm0_pll",
 181                .parent_names = (const char *[]){
 182                        "clk_ref2"
 183                },
 184                .num_parents = 1,
 185                .ops = &axxia_pllclk_ops,
 186        },
 187        .reg   = 0x03000,
 188};
 189
 190static struct axxia_pllclk clk_sm1_pll = {
 191        .aclk.hw.init = &(struct clk_init_data){
 192                .name = "clk_sm1_pll",
 193                .parent_names = (const char *[]){
 194                        "clk_ref1"
 195                },
 196                .num_parents = 1,
 197                .ops = &axxia_pllclk_ops,
 198        },
 199        .reg   = 0x03800,
 200};
 201
 202/*
 203 * Clock dividers
 204 */
 205
 206static struct axxia_divclk clk_cpu0_div = {
 207        .aclk.hw.init = &(struct clk_init_data){
 208                .name = "clk_cpu0_div",
 209                .parent_names = (const char *[]){
 210                        "clk_cpu_pll"
 211                },
 212                .num_parents = 1,
 213                .ops = &axxia_divclk_ops,
 214        },
 215        .reg   = 0x10008,
 216        .shift = 0,
 217        .width = 4,
 218};
 219
 220static struct axxia_divclk clk_cpu1_div = {
 221        .aclk.hw.init = &(struct clk_init_data){
 222                .name = "clk_cpu1_div",
 223                .parent_names = (const char *[]){
 224                        "clk_cpu_pll"
 225                },
 226                .num_parents = 1,
 227                .ops = &axxia_divclk_ops,
 228        },
 229        .reg   = 0x10008,
 230        .shift = 4,
 231        .width = 4,
 232};
 233
 234static struct axxia_divclk clk_cpu2_div = {
 235        .aclk.hw.init = &(struct clk_init_data){
 236                .name = "clk_cpu2_div",
 237                .parent_names = (const char *[]){
 238                        "clk_cpu_pll"
 239                },
 240                .num_parents = 1,
 241                .ops = &axxia_divclk_ops,
 242        },
 243        .reg   = 0x10008,
 244        .shift = 8,
 245        .width = 4,
 246};
 247
 248static struct axxia_divclk clk_cpu3_div = {
 249        .aclk.hw.init = &(struct clk_init_data){
 250                .name = "clk_cpu3_div",
 251                .parent_names = (const char *[]){
 252                        "clk_cpu_pll"
 253                },
 254                .num_parents = 1,
 255                .ops = &axxia_divclk_ops,
 256        },
 257        .reg   = 0x10008,
 258        .shift = 12,
 259        .width = 4,
 260};
 261
 262static struct axxia_divclk clk_nrcp_div = {
 263        .aclk.hw.init = &(struct clk_init_data){
 264                .name = "clk_nrcp_div",
 265                .parent_names = (const char *[]){
 266                        "clk_sys_pll"
 267                },
 268                .num_parents = 1,
 269                .ops = &axxia_divclk_ops,
 270        },
 271        .reg   = 0x1000c,
 272        .shift = 0,
 273        .width = 4,
 274};
 275
 276static struct axxia_divclk clk_sys_div = {
 277        .aclk.hw.init = &(struct clk_init_data){
 278                .name = "clk_sys_div",
 279                .parent_names = (const char *[]){
 280                        "clk_sys_pll"
 281                },
 282                .num_parents = 1,
 283                .ops = &axxia_divclk_ops,
 284        },
 285        .reg   = 0x1000c,
 286        .shift = 4,
 287        .width = 4,
 288};
 289
 290static struct axxia_divclk clk_fab_div = {
 291        .aclk.hw.init = &(struct clk_init_data){
 292                .name = "clk_fab_div",
 293                .parent_names = (const char *[]){
 294                        "clk_fab_pll"
 295                },
 296                .num_parents = 1,
 297                .ops = &axxia_divclk_ops,
 298        },
 299        .reg   = 0x1000c,
 300        .shift = 8,
 301        .width = 4,
 302};
 303
 304static struct axxia_divclk clk_per_div = {
 305        .aclk.hw.init = &(struct clk_init_data){
 306                .name = "clk_per_div",
 307                .parent_names = (const char *[]){
 308                        "clk_sm1_pll"
 309                },
 310                .num_parents = 1,
 311                .ops = &axxia_divclk_ops,
 312        },
 313        .reg   = 0x1000c,
 314        .shift = 12,
 315        .width = 4,
 316};
 317
 318static struct axxia_divclk clk_mmc_div = {
 319        .aclk.hw.init = &(struct clk_init_data){
 320                .name = "clk_mmc_div",
 321                .parent_names = (const char *[]){
 322                        "clk_sm1_pll"
 323                },
 324                .num_parents = 1,
 325                .ops = &axxia_divclk_ops,
 326        },
 327        .reg   = 0x1000c,
 328        .shift = 16,
 329        .width = 4,
 330};
 331
 332/*
 333 * Clock MUXes
 334 */
 335
 336static struct axxia_clkmux clk_cpu0_mux = {
 337        .aclk.hw.init = &(struct clk_init_data){
 338                .name = "clk_cpu0",
 339                .parent_names = (const char *[]){
 340                        "clk_ref0",
 341                        "clk_cpu_pll",
 342                        "clk_cpu0_div",
 343                        "clk_cpu0_div"
 344                },
 345                .num_parents = 4,
 346                .ops = &axxia_clkmux_ops,
 347        },
 348        .reg   = 0x10000,
 349        .shift = 0,
 350        .width = 2,
 351};
 352
 353static struct axxia_clkmux clk_cpu1_mux = {
 354        .aclk.hw.init = &(struct clk_init_data){
 355                .name = "clk_cpu1",
 356                .parent_names = (const char *[]){
 357                        "clk_ref0",
 358                        "clk_cpu_pll",
 359                        "clk_cpu1_div",
 360                        "clk_cpu1_div"
 361                },
 362                .num_parents = 4,
 363                .ops = &axxia_clkmux_ops,
 364        },
 365        .reg   = 0x10000,
 366        .shift = 2,
 367        .width = 2,
 368};
 369
 370static struct axxia_clkmux clk_cpu2_mux = {
 371        .aclk.hw.init = &(struct clk_init_data){
 372                .name = "clk_cpu2",
 373                .parent_names = (const char *[]){
 374                        "clk_ref0",
 375                        "clk_cpu_pll",
 376                        "clk_cpu2_div",
 377                        "clk_cpu2_div"
 378                },
 379                .num_parents = 4,
 380                .ops = &axxia_clkmux_ops,
 381        },
 382        .reg   = 0x10000,
 383        .shift = 4,
 384        .width = 2,
 385};
 386
 387static struct axxia_clkmux clk_cpu3_mux = {
 388        .aclk.hw.init = &(struct clk_init_data){
 389                .name = "clk_cpu3",
 390                .parent_names = (const char *[]){
 391                        "clk_ref0",
 392                        "clk_cpu_pll",
 393                        "clk_cpu3_div",
 394                        "clk_cpu3_div"
 395                },
 396                .num_parents = 4,
 397                .ops = &axxia_clkmux_ops,
 398        },
 399        .reg   = 0x10000,
 400        .shift = 6,
 401        .width = 2,
 402};
 403
 404static struct axxia_clkmux clk_nrcp_mux = {
 405        .aclk.hw.init = &(struct clk_init_data){
 406                .name = "clk_nrcp",
 407                .parent_names = (const char *[]){
 408                        "clk_ref0",
 409                        "clk_sys_pll",
 410                        "clk_nrcp_div",
 411                        "clk_nrcp_div"
 412                },
 413                .num_parents = 4,
 414                .ops = &axxia_clkmux_ops,
 415        },
 416        .reg   = 0x10004,
 417        .shift = 0,
 418        .width = 2,
 419};
 420
 421static struct axxia_clkmux clk_sys_mux = {
 422        .aclk.hw.init = &(struct clk_init_data){
 423                .name = "clk_sys",
 424                .parent_names = (const char *[]){
 425                        "clk_ref0",
 426                        "clk_sys_pll",
 427                        "clk_sys_div",
 428                        "clk_sys_div"
 429                },
 430                .num_parents = 4,
 431                .ops = &axxia_clkmux_ops,
 432        },
 433        .reg   = 0x10004,
 434        .shift = 2,
 435        .width = 2,
 436};
 437
 438static struct axxia_clkmux clk_fab_mux = {
 439        .aclk.hw.init = &(struct clk_init_data){
 440                .name = "clk_fab",
 441                .parent_names = (const char *[]){
 442                        "clk_ref0",
 443                        "clk_fab_pll",
 444                        "clk_fab_div",
 445                        "clk_fab_div"
 446                },
 447                .num_parents = 4,
 448                .ops = &axxia_clkmux_ops,
 449        },
 450        .reg   = 0x10004,
 451        .shift = 4,
 452        .width = 2,
 453};
 454
 455static struct axxia_clkmux clk_per_mux = {
 456        .aclk.hw.init = &(struct clk_init_data){
 457                .name = "clk_per",
 458                .parent_names = (const char *[]){
 459                        "clk_ref1",
 460                        "clk_per_div"
 461                },
 462                .num_parents = 2,
 463                .ops = &axxia_clkmux_ops,
 464        },
 465        .reg   = 0x10004,
 466        .shift = 6,
 467        .width = 1,
 468};
 469
 470static struct axxia_clkmux clk_mmc_mux = {
 471        .aclk.hw.init = &(struct clk_init_data){
 472                .name = "clk_mmc",
 473                .parent_names = (const char *[]){
 474                        "clk_ref1",
 475                        "clk_mmc_div"
 476                },
 477                .num_parents = 2,
 478                .ops = &axxia_clkmux_ops,
 479        },
 480        .reg   = 0x10004,
 481        .shift = 9,
 482        .width = 1,
 483};
 484
 485/* Table of all supported clocks indexed by the clock identifiers from the
 486 * device tree binding
 487 */
 488static struct axxia_clk *axmclk_clocks[] = {
 489        [AXXIA_CLK_FAB_PLL]  = &clk_fab_pll.aclk,
 490        [AXXIA_CLK_CPU_PLL]  = &clk_cpu_pll.aclk,
 491        [AXXIA_CLK_SYS_PLL]  = &clk_sys_pll.aclk,
 492        [AXXIA_CLK_SM0_PLL]  = &clk_sm0_pll.aclk,
 493        [AXXIA_CLK_SM1_PLL]  = &clk_sm1_pll.aclk,
 494        [AXXIA_CLK_FAB_DIV]  = &clk_fab_div.aclk,
 495        [AXXIA_CLK_SYS_DIV]  = &clk_sys_div.aclk,
 496        [AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
 497        [AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
 498        [AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
 499        [AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
 500        [AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
 501        [AXXIA_CLK_PER_DIV]  = &clk_per_div.aclk,
 502        [AXXIA_CLK_MMC_DIV]  = &clk_mmc_div.aclk,
 503        [AXXIA_CLK_FAB]      = &clk_fab_mux.aclk,
 504        [AXXIA_CLK_SYS]      = &clk_sys_mux.aclk,
 505        [AXXIA_CLK_NRCP]     = &clk_nrcp_mux.aclk,
 506        [AXXIA_CLK_CPU0]     = &clk_cpu0_mux.aclk,
 507        [AXXIA_CLK_CPU1]     = &clk_cpu1_mux.aclk,
 508        [AXXIA_CLK_CPU2]     = &clk_cpu2_mux.aclk,
 509        [AXXIA_CLK_CPU3]     = &clk_cpu3_mux.aclk,
 510        [AXXIA_CLK_PER]      = &clk_per_mux.aclk,
 511        [AXXIA_CLK_MMC]      = &clk_mmc_mux.aclk,
 512};
 513
 514static struct clk_hw *
 515of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)
 516{
 517        unsigned int idx = clkspec->args[0];
 518
 519        if (idx >= ARRAY_SIZE(axmclk_clocks)) {
 520                pr_err("%s: invalid index %u\n", __func__, idx);
 521                return ERR_PTR(-EINVAL);
 522        }
 523
 524        return &axmclk_clocks[idx]->hw;
 525}
 526
 527static const struct regmap_config axmclk_regmap_config = {
 528        .reg_bits       = 32,
 529        .reg_stride     = 4,
 530        .val_bits       = 32,
 531        .max_register   = 0x1fffc,
 532        .fast_io        = true,
 533};
 534
 535static const struct of_device_id axmclk_match_table[] = {
 536        { .compatible = "lsi,axm5516-clks" },
 537        { }
 538};
 539MODULE_DEVICE_TABLE(of, axmclk_match_table);
 540
 541static int axmclk_probe(struct platform_device *pdev)
 542{
 543        void __iomem *base;
 544        struct resource *res;
 545        int i, ret;
 546        struct device *dev = &pdev->dev;
 547        struct regmap *regmap;
 548        size_t num_clks;
 549
 550        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 551        base = devm_ioremap_resource(dev, res);
 552        if (IS_ERR(base))
 553                return PTR_ERR(base);
 554
 555        regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
 556        if (IS_ERR(regmap))
 557                return PTR_ERR(regmap);
 558
 559        num_clks = ARRAY_SIZE(axmclk_clocks);
 560        pr_info("axmclk: supporting %zu clocks\n", num_clks);
 561
 562        /* Update each entry with the allocated regmap and register the clock
 563         * with the common clock framework
 564         */
 565        for (i = 0; i < num_clks; i++) {
 566                axmclk_clocks[i]->regmap = regmap;
 567                ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);
 568                if (ret)
 569                        return ret;
 570        }
 571
 572        return of_clk_add_hw_provider(dev->of_node, of_clk_axmclk_get, NULL);
 573}
 574
 575static int axmclk_remove(struct platform_device *pdev)
 576{
 577        of_clk_del_provider(pdev->dev.of_node);
 578        return 0;
 579}
 580
 581static struct platform_driver axmclk_driver = {
 582        .probe          = axmclk_probe,
 583        .remove         = axmclk_remove,
 584        .driver         = {
 585                .name   = "clk-axm5516",
 586                .of_match_table = axmclk_match_table,
 587        },
 588};
 589
 590static int __init axmclk_init(void)
 591{
 592        return platform_driver_register(&axmclk_driver);
 593}
 594core_initcall(axmclk_init);
 595
 596static void __exit axmclk_exit(void)
 597{
 598        platform_driver_unregister(&axmclk_driver);
 599}
 600module_exit(axmclk_exit);
 601
 602MODULE_DESCRIPTION("AXM5516 clock driver");
 603MODULE_LICENSE("GPL v2");
 604MODULE_ALIAS("platform:clk-axm5516");
 605