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 inline int get_mux(struct a37xx_periphclk *priv, int shift)
 228{
 229        return (readl(priv->reg + TBG_SEL) >> shift) & 3;
 230}
 231
 232static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
 233
 234static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
 235{
 236        const struct clk_periph *clk = &priv->clks[id];
 237        ulong res;
 238
 239        if (clk->can_mux) {
 240                /* parent is one of TBG clocks */
 241                int tbg = get_mux(priv, clk->mux_shift);
 242
 243                res = priv->parents[tbg];
 244        } else if (priv->clk_has_periph_parent[id]) {
 245                /* parent is one of other periph clocks */
 246
 247                if (priv->clk_parent[id] >= priv->count)
 248                        return -EINVAL;
 249
 250                res = periph_clk_get_rate(priv, priv->clk_parent[id]);
 251        } else {
 252                /* otherwise parent is one of TBGs or XTAL */
 253
 254                if (priv->clk_parent[id] >= MAX_PARENTS)
 255                        return -EINVAL;
 256
 257                res = priv->parents[priv->clk_parent[id]];
 258        }
 259
 260        return res;
 261}
 262
 263static ulong get_div(struct a37xx_periphclk *priv,
 264                     const struct clk_periph *clk, int idx)
 265{
 266        const struct clk_div_table *i;
 267        u32 reg;
 268
 269        reg = readl(priv->reg + clk->div_reg_off[idx]);
 270        reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
 271
 272        /* find divisor for register value val */
 273        for (i = clk->div_table[idx]; i && i->div != 0; ++i)
 274                if (i->val == reg)
 275                        return i->div;
 276
 277        return 0;
 278}
 279
 280static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
 281{
 282        const struct clk_periph *clk = &priv->clks[id];
 283        ulong rate, div;
 284        int i;
 285
 286        rate = get_parent_rate(priv, id);
 287        if (rate == -EINVAL)
 288                return -EINVAL;
 289
 290        /* divide the parent rate by dividers */
 291        div = 1;
 292        for (i = 0; i < clk->dividers; ++i)
 293                div *= get_div(priv, clk, i);
 294
 295        if (!div)
 296                return 0;
 297
 298        return DIV_ROUND_UP(rate, div);
 299}
 300
 301static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
 302{
 303        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 304
 305        if (clk->id >= priv->count)
 306                return -EINVAL;
 307
 308        return periph_clk_get_rate(priv, clk->id);
 309}
 310
 311static int periph_clk_enable(struct clk *clk, int enable)
 312{
 313        struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
 314        const struct clk_periph *periph_clk = &priv->clks[clk->id];
 315
 316        if (clk->id >= priv->count)
 317                return -EINVAL;
 318
 319        if (!periph_clk->can_gate)
 320                return -ENOTSUPP;
 321
 322        if (enable)
 323                clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
 324        else
 325                setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
 326
 327        return 0;
 328}
 329
 330static int armada_37xx_periph_clk_enable(struct clk *clk)
 331{
 332        return periph_clk_enable(clk, 1);
 333}
 334
 335static int armada_37xx_periph_clk_disable(struct clk *clk)
 336{
 337        return periph_clk_enable(clk, 0);
 338}
 339
 340#if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
 341static int armada_37xx_periph_clk_dump(struct udevice *dev)
 342{
 343        struct a37xx_periphclk *priv = dev_get_priv(dev);
 344        const struct clk_periph *clks;
 345        int i;
 346
 347        if (!priv)
 348                return -ENODEV;
 349
 350        clks = priv->clks;
 351
 352        for (i = 0; i < priv->count; ++i)
 353                printf("  %s at %lu Hz\n", clks[i].name,
 354                       periph_clk_get_rate(priv, i));
 355        printf("\n");
 356
 357        return 0;
 358}
 359
 360static int clk_dump(const char *name, int (*func)(struct udevice *))
 361{
 362        struct udevice *dev;
 363
 364        if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
 365                printf("Cannot find device %s\n", name);
 366                return -ENODEV;
 367        }
 368
 369        return func(dev);
 370}
 371
 372int armada_37xx_tbg_clk_dump(struct udevice *);
 373
 374int soc_clk_dump(void)
 375{
 376        printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
 377
 378        if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
 379                return 1;
 380
 381        if (clk_dump("nb-periph-clk@13000",
 382                     armada_37xx_periph_clk_dump))
 383                return 1;
 384
 385        if (clk_dump("sb-periph-clk@18000",
 386                     armada_37xx_periph_clk_dump))
 387                return 1;
 388
 389        return 0;
 390}
 391#endif
 392
 393static int armada_37xx_periph_clk_probe(struct udevice *dev)
 394{
 395        struct a37xx_periphclk *priv = dev_get_priv(dev);
 396        const struct clk_periph *clks;
 397        int ret, i;
 398
 399        clks = (const struct clk_periph *)dev_get_driver_data(dev);
 400        if (!clks)
 401                return -ENODEV;
 402
 403        priv->reg = dev_read_addr_ptr(dev);
 404        if (!priv->reg) {
 405                dev_err(dev, "no io address\n");
 406                return -ENODEV;
 407        }
 408
 409        /* count clk_periph nodes */
 410        priv->count = 0;
 411        while (clks[priv->count].name)
 412                priv->count++;
 413
 414        priv->clks = clks;
 415
 416        /* assign parent IDs to nodes which have non-NULL parent_name */
 417        for (i = 0; i < priv->count; ++i) {
 418                int j;
 419
 420                if (!clks[i].parent_name)
 421                        continue;
 422
 423                /* first try if parent_name is one of TBGs or XTAL */
 424                for (j = 0; j < MAX_PARENTS; ++j)
 425                        if (!strcmp(clks[i].parent_name,
 426                                    a37xx_periph_parent_names[j].name))
 427                                break;
 428
 429                if (j < MAX_PARENTS) {
 430                        priv->clk_has_periph_parent[i] = false;
 431                        priv->clk_parent[i] =
 432                                a37xx_periph_parent_names[j].parent;
 433                        continue;
 434                }
 435
 436                /* else parent_name should be one of other periph clocks */
 437                for (j = 0; j < priv->count; ++j) {
 438                        if (!strcmp(clks[i].parent_name, clks[j].name))
 439                                break;
 440                }
 441
 442                if (j < priv->count) {
 443                        priv->clk_has_periph_parent[i] = true;
 444                        priv->clk_parent[i] = j;
 445                        continue;
 446                }
 447
 448                dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
 449                return -EINVAL;
 450        }
 451
 452        for (i = 0; i < MAX_PARENTS; ++i) {
 453                struct clk clk;
 454
 455                if (i == XTAL) {
 456                        priv->parents[i] = get_ref_clk() * 1000000;
 457                        continue;
 458                }
 459
 460                ret = clk_get_by_index(dev, i, &clk);
 461                if (ret) {
 462                        dev_err(dev, "one of parent clocks (%i) missing: %i\n",
 463                                i, ret);
 464                        return -ENODEV;
 465                }
 466
 467                priv->parents[i] = clk_get_rate(&clk);
 468                clk_free(&clk);
 469        }
 470
 471        return 0;
 472}
 473
 474static const struct clk_ops armada_37xx_periph_clk_ops = {
 475        .get_rate = armada_37xx_periph_clk_get_rate,
 476        .enable = armada_37xx_periph_clk_enable,
 477        .disable = armada_37xx_periph_clk_disable,
 478};
 479
 480static const struct udevice_id armada_37xx_periph_clk_ids[] = {
 481        {
 482                .compatible = "marvell,armada-3700-periph-clock-nb",
 483                .data = (ulong)clks_nb,
 484        },
 485        {
 486                .compatible = "marvell,armada-3700-periph-clock-sb",
 487                .data = (ulong)clks_sb,
 488        },
 489        {}
 490};
 491
 492U_BOOT_DRIVER(armada_37xx_periph_clk) = {
 493        .name           = "armada_37xx_periph_clk",
 494        .id             = UCLASS_CLK,
 495        .of_match       = armada_37xx_periph_clk_ids,
 496        .ops            = &armada_37xx_periph_clk_ops,
 497        .priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
 498        .probe          = armada_37xx_periph_clk_probe,
 499};
 500