uboot/drivers/clk/mvebu/armada-37xx-periph.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Marvell Armada 37xx SoC Peripheral clocks
   4 *
   5 * Marek Behun <marek.behun@nic.cz>
   6 *
   7 * Based on Linux driver by:
   8 *   Gregory CLEMENT <gregory.clement@free-electrons.com>
   9 */
  10
  11#include <common.h>
  12#include <malloc.h>
  13#include <clk-uclass.h>
  14#include <clk.h>
  15#include <dm.h>
  16#include <asm/io.h>
  17#include <asm/arch/cpu.h>
  18
  19#define TBG_SEL         0x0
  20#define DIV_SEL0        0x4
  21#define DIV_SEL1        0x8
  22#define DIV_SEL2        0xC
  23#define CLK_SEL         0x10
  24#define CLK_DIS         0x14
  25
  26enum a37xx_periph_parent {
  27        TBG_A_P         = 0,
  28        TBG_B_P         = 1,
  29        TBG_A_S         = 2,
  30        TBG_B_S         = 3,
  31        MAX_TBG_PARENTS = 4,
  32        XTAL            = 4,
  33        MAX_PARENTS     = 5,
  34};
  35
  36static const struct {
  37        const char *name;
  38        enum a37xx_periph_parent parent;
  39} a37xx_periph_parent_names[] = {
  40        { "TBG-A-P", TBG_A_P },
  41        { "TBG-B-P", TBG_B_P },
  42        { "TBG-A-S", TBG_A_S },
  43        { "TBG-B-S", TBG_B_S },
  44        { "xtal",    XTAL    },
  45};
  46
  47struct clk_periph;
  48
  49struct a37xx_periphclk {
  50        void __iomem *reg;
  51
  52        ulong parents[MAX_PARENTS];
  53
  54        const struct clk_periph *clks;
  55        bool clk_has_periph_parent[16];
  56        int clk_parent[16];
  57
  58        int count;
  59};
  60
  61struct clk_div_table {
  62        u32 div;
  63        u32 val;
  64};
  65
  66struct clk_periph {
  67        const char *name;
  68
  69        const char *parent_name;
  70
  71        u32 disable_bit;
  72        int mux_shift;
  73
  74        const struct clk_div_table *div_table[2];
  75        s32 div_reg_off[2];
  76        u32 div_mask[2];
  77        int div_shift[2];
  78
  79        unsigned can_gate : 1;
  80        unsigned can_mux : 1;
  81        unsigned dividers : 2;
  82};
  83
  84static const struct clk_div_table div_table1[] = {
  85        { 1, 1 },
  86        { 2, 2 },
  87        { 0, 0 },
  88};
  89
  90static const struct clk_div_table div_table2[] = {
  91        { 2, 1 },
  92        { 4, 2 },
  93        { 0, 0 },
  94};
  95
  96static const struct clk_div_table div_table6[] = {
  97        { 1, 1 },
  98        { 2, 2 },
  99        { 3, 3 },
 100        { 4, 4 },
 101        { 5, 5 },
 102        { 6, 6 },
 103        { 0, 0 },
 104};
 105
 106#define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1)   \
 107        {                                               \
 108                .name = #_n,                            \
 109                .disable_bit = BIT(_d),                 \
 110                .mux_shift = _mux,                      \
 111                .div_table[0] = div_table6,             \
 112                .div_table[1] = div_table6,             \
 113                .div_reg_off[0] = _r0,                  \
 114                .div_reg_off[1] = _r1,                  \
 115                .div_shift[0] = _s0,                    \
 116                .div_shift[1] = _s1,                    \
 117                .div_mask[0] = 7,                       \
 118                .div_mask[1] = 7,                       \
 119                .can_gate = 1,                          \
 120                .can_mux = 1,                           \
 121                .dividers = 2,                          \
 122        }
 123
 124#define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t)  \
 125        {                                       \
 126                .name = #_n,                    \
 127                .disable_bit = BIT(_d),         \
 128                .mux_shift = _mux,              \
 129                .div_table[0] = _t,             \
 130                .div_reg_off[0] = _r,           \
 131                .div_shift[0] = _s,             \
 132                .div_mask[0] = _m,              \
 133                .can_gate = 1,                  \
 134                .can_mux = 1,                   \
 135                .dividers = 1,                  \
 136        }
 137
 138#define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p)        \
 139        {                                               \
 140                .name = #_n,                            \
 141                .parent_name = _p,                      \
 142                .disable_bit = BIT(_d),                 \
 143                .div_table[0] = _t,                     \
 144                .div_reg_off[0] = _r,                   \
 145                .div_shift[0] = _s,                     \
 146                .div_mask[0] = _m,                      \
 147                .can_gate = 1,                          \
 148                .dividers = 1,                          \
 149        }
 150
 151#define CLK_GATE(_n, _d, _p)            \
 152        {                               \
 153                .name = #_n,            \
 154                .parent_name = _p,      \
 155                .disable_bit = BIT(_d), \
 156                .can_gate = 1,          \
 157        }
 158
 159#define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t)   \
 160        {                                       \
 161                .name = #_n,                    \
 162                .mux_shift = _mux,              \
 163                .div_table[0] = _t,             \
 164                .div_reg_off[0] = _r,           \
 165                .div_shift[0] = _s,             \
 166                .div_mask[0] = _m,              \
 167                .can_mux = 1,                   \
 168                .dividers = 1,                  \
 169        }
 170
 171#define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1)        \
 172        {                                               \
 173                .name = #_n,                            \
 174                .mux_shift = _mux,                      \
 175                .div_table[0] = div_table6,             \
 176                .div_table[1] = div_table6,             \
 177                .div_reg_off[0] = _r0,                  \
 178                .div_reg_off[1] = _r1,                  \
 179                .div_shift[0] = _s0,                    \
 180                .div_shift[1] = _s1,                    \
 181                .div_mask[0] = 7,                       \
 182                .div_mask[1] = 7,                       \
 183                .can_mux = 1,                           \
 184                .dividers = 2,                          \
 185        }
 186
 187/* NB periph clocks */
 188static const struct clk_periph clks_nb[] = {
 189        CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
 190        CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
 191        CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
 192        CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
 193        CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
 194        CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
 195        CLK_GATE(avs, 11, "xtal"),
 196        CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
 197        CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
 198        CLK_GATE(i2c_2, 16, "xtal"),
 199        CLK_GATE(i2c_1, 17, "xtal"),
 200        CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
 201        CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
 202        CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
 203        CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
 204        CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
 205        CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
 206        { },
 207};
 208
 209/* SB periph clocks */
 210static const struct clk_periph clks_sb[] = {
 211        CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
 212        CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
 213        CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
 214        CLK_GATE(gbe1_50, 0, "gbe_50"),
 215        CLK_GATE(gbe0_50, 1, "gbe_50"),
 216        CLK_GATE(gbe1_125, 2, "gbe_125"),
 217        CLK_GATE(gbe0_125, 3, "gbe_125"),
 218        CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
 219        CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
 220        CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
 221        CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
 222        CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
 223        CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
 224        { },
 225};
 226
 227static int get_mux(struct a37xx_periphclk *priv, int shift)
 228{
 229        return (readl(priv->reg + TBG_SEL) >> shift) & 3;
 230}
 231
 232static void set_mux(struct a37xx_periphclk *priv, int shift, int val)
 233{
 234        u32 reg;
 235
 236        reg = readl(priv->reg + TBG_SEL);
 237        reg &= ~(3 << shift);
 238        reg |= (val & 3) << shift;
 239        writel(reg, priv->reg + TBG_SEL);
 240}
 241
 242static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
 243
 244static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
 245{
 246        const struct clk_periph *clk = &priv->clks[id];
 247        ulong res;
 248
 249        if (clk->can_mux) {
 250                /* parent is one of TBG clocks */
 251                int tbg = get_mux(priv, clk->mux_shift);
 252
 253                res = priv->parents[tbg];
 254        } else if (priv->clk_has_periph_parent[id]) {
 255                /* parent is one of other periph clocks */
 256
 257                if (priv->clk_parent[id] >= priv->count)
 258                        return -EINVAL;
 259
 260                res = periph_clk_get_rate(priv, priv->clk_parent[id]);
 261        } else {
 262                /* otherwise parent is one of TBGs or XTAL */
 263
 264                if (priv->clk_parent[id] >= MAX_PARENTS)
 265                        return -EINVAL;
 266
 267                res = priv->parents[priv->clk_parent[id]];
 268        }
 269
 270        return res;
 271}
 272
 273static ulong get_div(struct a37xx_periphclk *priv,
 274                     const struct clk_periph *clk, int idx)
 275{
 276        const struct clk_div_table *i;
 277        u32 reg;
 278
 279        reg = readl(priv->reg + clk->div_reg_off[idx]);
 280        reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
 281
 282        /* find divisor for register value val */
 283        for (i = clk->div_table[idx]; i && i->div != 0; ++i)
 284                if (i->val == reg)
 285                        return i->div;
 286
 287        return 0;
 288}
 289
 290static void set_div_val(struct a37xx_periphclk *priv,
 291                        const struct clk_periph *clk, int idx, int val)
 292{
 293        u32 reg;
 294
 295        reg = readl(priv->reg + clk->div_reg_off[idx]);
 296        reg &= ~(clk->div_mask[idx] << clk->div_shift[idx]);
 297        reg |= (val & clk->div_mask[idx]) << clk->div_shift[idx];
 298        writel(reg, priv->reg + clk->div_reg_off[idx]);
 299}
 300
 301static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
 302{
 303        const struct clk_periph *clk = &priv->clks[id];
 304        ulong rate, div;
 305        int i;
 306
 307        rate = get_parent_rate(priv, id);
 308        if (rate == -EINVAL)
 309                return -EINVAL;
 310
 311        /* divide the parent rate by dividers */
 312        div = 1;
 313        for (i = 0; i < clk->dividers; ++i)
 314                div *= get_div(priv, clk, i);
 315
 316        if (!div)
 317                return 0;
 318
 319        return DIV_ROUND_UP(rate, div);
 320}
 321
 322static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
 323{
 324        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 325
 326        if (clk->id >= priv->count)
 327                return -EINVAL;
 328
 329        return periph_clk_get_rate(priv, clk->id);
 330}
 331
 332static int periph_clk_enable(struct clk *clk, int enable)
 333{
 334        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 335        const struct clk_periph *periph_clk = &priv->clks[clk->id];
 336
 337        if (clk->id >= priv->count)
 338                return -EINVAL;
 339
 340        if (!periph_clk->can_gate)
 341                return -ENOTSUPP;
 342
 343        if (enable)
 344                clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
 345        else
 346                setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
 347
 348        return 0;
 349}
 350
 351static int armada_37xx_periph_clk_enable(struct clk *clk)
 352{
 353        return periph_clk_enable(clk, 1);
 354}
 355
 356static int armada_37xx_periph_clk_disable(struct clk *clk)
 357{
 358        return periph_clk_enable(clk, 0);
 359}
 360
 361#define diff(a, b) abs((long)(a) - (long)(b))
 362
 363static ulong find_best_div(const struct clk_div_table *t0,
 364                           const struct clk_div_table *t1, ulong parent_rate,
 365                           ulong req_rate, int *v0, int *v1)
 366{
 367        const struct clk_div_table *i, *j;
 368        ulong rate, best_rate = 0;
 369
 370        for (i = t0; i && i->div; ++i) {
 371                for (j = t1; j && j->div; ++j) {
 372                        rate = DIV_ROUND_UP(parent_rate, i->div * j->div);
 373
 374                        if (!best_rate ||
 375                            diff(rate, req_rate) < diff(best_rate, req_rate)) {
 376                                best_rate = rate;
 377                                *v0 = i->val;
 378                                *v1 = j->val;
 379                        }
 380                }
 381        }
 382
 383        return best_rate;
 384}
 385
 386static ulong armada_37xx_periph_clk_set_rate(struct clk *clk, ulong req_rate)
 387{
 388        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 389        const struct clk_periph *periph_clk = &priv->clks[clk->id];
 390        ulong rate, old_rate, parent_rate;
 391        int div_val0 = 0, div_val1 = 0;
 392        const struct clk_div_table *t1;
 393        static const struct clk_div_table empty_table[2] = {
 394                { 1, 0 },
 395                { 0, 0 }
 396        };
 397
 398        if (clk->id > priv->count)
 399                return -EINVAL;
 400
 401        old_rate = periph_clk_get_rate(priv, clk->id);
 402        if (old_rate == -EINVAL)
 403                return -EINVAL;
 404
 405        if (old_rate == req_rate)
 406                return old_rate;
 407
 408        if (!periph_clk->can_gate || !periph_clk->dividers)
 409                return -ENOTSUPP;
 410
 411        parent_rate = get_parent_rate(priv, clk->id);
 412        if (parent_rate == -EINVAL)
 413                return -EINVAL;
 414
 415        t1 = empty_table;
 416        if (periph_clk->dividers > 1)
 417                t1 = periph_clk->div_table[1];
 418
 419        rate = find_best_div(periph_clk->div_table[0], t1, parent_rate,
 420                             req_rate, &div_val0, &div_val1);
 421
 422        periph_clk_enable(clk, 0);
 423
 424        set_div_val(priv, periph_clk, 0, div_val0);
 425        if (periph_clk->dividers > 1)
 426                set_div_val(priv, periph_clk, 1, div_val1);
 427
 428        periph_clk_enable(clk, 1);
 429
 430        return rate;
 431}
 432
 433static int armada_37xx_periph_clk_set_parent(struct clk *clk,
 434                                             struct clk *parent)
 435{
 436        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 437        const struct clk_periph *periph_clk = &priv->clks[clk->id];
 438        struct clk check_parent;
 439        int ret;
 440
 441        /* We also check if parent is our TBG clock */
 442        if (clk->id > priv->count || parent->id >= MAX_TBG_PARENTS)
 443                return -EINVAL;
 444
 445        if (!periph_clk->can_mux || !periph_clk->can_gate)
 446                return -ENOTSUPP;
 447
 448        ret = clk_get_by_index(clk->dev, 0, &check_parent);
 449        if (ret < 0)
 450                return ret;
 451
 452        if (parent->dev != check_parent.dev)
 453                ret = -EINVAL;
 454
 455        clk_free(&check_parent);
 456        if (ret < 0)
 457                return ret;
 458
 459        periph_clk_enable(clk, 0);
 460        set_mux(priv, periph_clk->mux_shift, parent->id);
 461        periph_clk_enable(clk, 1);
 462
 463        return 0;
 464}
 465
 466#if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
 467static int armada_37xx_periph_clk_dump(struct udevice *dev)
 468{
 469        struct a37xx_periphclk *priv = dev_get_priv(dev);
 470        const struct clk_periph *clks;
 471        int i;
 472
 473        if (!priv)
 474                return -ENODEV;
 475
 476        clks = priv->clks;
 477
 478        for (i = 0; i < priv->count; ++i)
 479                printf("  %s at %lu Hz\n", clks[i].name,
 480                       periph_clk_get_rate(priv, i));
 481        printf("\n");
 482
 483        return 0;
 484}
 485
 486static int clk_dump(const char *name, int (*func)(struct udevice *))
 487{
 488        struct udevice *dev;
 489
 490        if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
 491                printf("Cannot find device %s\n", name);
 492                return -ENODEV;
 493        }
 494
 495        return func(dev);
 496}
 497
 498int armada_37xx_tbg_clk_dump(struct udevice *);
 499
 500int soc_clk_dump(void)
 501{
 502        printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
 503
 504        if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
 505                return 1;
 506
 507        if (clk_dump("nb-periph-clk@13000",
 508                     armada_37xx_periph_clk_dump))
 509                return 1;
 510
 511        if (clk_dump("sb-periph-clk@18000",
 512                     armada_37xx_periph_clk_dump))
 513                return 1;
 514
 515        return 0;
 516}
 517#endif
 518
 519static int armada_37xx_periph_clk_probe(struct udevice *dev)
 520{
 521        struct a37xx_periphclk *priv = dev_get_priv(dev);
 522        const struct clk_periph *clks;
 523        int ret, i;
 524
 525        clks = (const struct clk_periph *)dev_get_driver_data(dev);
 526        if (!clks)
 527                return -ENODEV;
 528
 529        priv->reg = dev_read_addr_ptr(dev);
 530        if (!priv->reg) {
 531                dev_err(dev, "no io address\n");
 532                return -ENODEV;
 533        }
 534
 535        /* count clk_periph nodes */
 536        priv->count = 0;
 537        while (clks[priv->count].name)
 538                priv->count++;
 539
 540        priv->clks = clks;
 541
 542        /* assign parent IDs to nodes which have non-NULL parent_name */
 543        for (i = 0; i < priv->count; ++i) {
 544                int j;
 545
 546                if (!clks[i].parent_name)
 547                        continue;
 548
 549                /* first try if parent_name is one of TBGs or XTAL */
 550                for (j = 0; j < MAX_PARENTS; ++j)
 551                        if (!strcmp(clks[i].parent_name,
 552                                    a37xx_periph_parent_names[j].name))
 553                                break;
 554
 555                if (j < MAX_PARENTS) {
 556                        priv->clk_has_periph_parent[i] = false;
 557                        priv->clk_parent[i] =
 558                                a37xx_periph_parent_names[j].parent;
 559                        continue;
 560                }
 561
 562                /* else parent_name should be one of other periph clocks */
 563                for (j = 0; j < priv->count; ++j) {
 564                        if (!strcmp(clks[i].parent_name, clks[j].name))
 565                                break;
 566                }
 567
 568                if (j < priv->count) {
 569                        priv->clk_has_periph_parent[i] = true;
 570                        priv->clk_parent[i] = j;
 571                        continue;
 572                }
 573
 574                dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
 575                return -EINVAL;
 576        }
 577
 578        for (i = 0; i < MAX_PARENTS; ++i) {
 579                struct clk clk;
 580
 581                if (i == XTAL) {
 582                        priv->parents[i] = get_ref_clk() * 1000000;
 583                        continue;
 584                }
 585
 586                ret = clk_get_by_index(dev, i, &clk);
 587                if (ret) {
 588                        dev_err(dev, "one of parent clocks (%i) missing: %i\n",
 589                                i, ret);
 590                        return -ENODEV;
 591                }
 592
 593                priv->parents[i] = clk_get_rate(&clk);
 594                clk_free(&clk);
 595        }
 596
 597        return 0;
 598}
 599
 600static const struct clk_ops armada_37xx_periph_clk_ops = {
 601        .get_rate = armada_37xx_periph_clk_get_rate,
 602        .set_rate = armada_37xx_periph_clk_set_rate,
 603        .set_parent = armada_37xx_periph_clk_set_parent,
 604        .enable = armada_37xx_periph_clk_enable,
 605        .disable = armada_37xx_periph_clk_disable,
 606};
 607
 608static const struct udevice_id armada_37xx_periph_clk_ids[] = {
 609        {
 610                .compatible = "marvell,armada-3700-periph-clock-nb",
 611                .data = (ulong)clks_nb,
 612        },
 613        {
 614                .compatible = "marvell,armada-3700-periph-clock-sb",
 615                .data = (ulong)clks_sb,
 616        },
 617        {}
 618};
 619
 620U_BOOT_DRIVER(armada_37xx_periph_clk) = {
 621        .name           = "armada_37xx_periph_clk",
 622        .id             = UCLASS_CLK,
 623        .of_match       = armada_37xx_periph_clk_ids,
 624        .ops            = &armada_37xx_periph_clk_ops,
 625        .priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
 626        .probe          = armada_37xx_periph_clk_probe,
 627};
 628