uboot/drivers/clk/rockchip/clk_rk3368.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2017 Rockchip Electronics Co., Ltd
   3 * Author: Andy Yan <andy.yan@rock-chips.com>
   4 * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
   5 * SPDX-License-Identifier:     GPL-2.0
   6 */
   7
   8#include <common.h>
   9#include <clk-uclass.h>
  10#include <dm.h>
  11#include <dt-structs.h>
  12#include <errno.h>
  13#include <mapmem.h>
  14#include <syscon.h>
  15#include <asm/arch/clock.h>
  16#include <asm/arch/cru_rk3368.h>
  17#include <asm/arch/hardware.h>
  18#include <asm/io.h>
  19#include <dm/lists.h>
  20#include <dt-bindings/clock/rk3368-cru.h>
  21
  22DECLARE_GLOBAL_DATA_PTR;
  23
  24#if CONFIG_IS_ENABLED(OF_PLATDATA)
  25struct rk3368_clk_plat {
  26        struct dtd_rockchip_rk3368_cru dtd;
  27};
  28#endif
  29
  30struct pll_div {
  31        u32 nr;
  32        u32 nf;
  33        u32 no;
  34};
  35
  36#define OSC_HZ          (24 * 1000 * 1000)
  37#define APLL_L_HZ       (800 * 1000 * 1000)
  38#define APLL_B_HZ       (816 * 1000 * 1000)
  39#define GPLL_HZ         (576 * 1000 * 1000)
  40#define CPLL_HZ         (400 * 1000 * 1000)
  41
  42#define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
  43
  44#define PLL_DIVISORS(hz, _nr, _no) { \
  45        .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no}; \
  46        _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
  47                       (_nr * _no) == hz, #hz "Hz cannot be hit with PLL " \
  48                       "divisors on line " __stringify(__LINE__));
  49
  50#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
  51static const struct pll_div apll_l_init_cfg = PLL_DIVISORS(APLL_L_HZ, 12, 2);
  52static const struct pll_div apll_b_init_cfg = PLL_DIVISORS(APLL_B_HZ, 1, 2);
  53#if !defined(CONFIG_TPL_BUILD)
  54static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 2);
  55static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6);
  56#endif
  57#endif
  58
  59static ulong rk3368_clk_get_rate(struct clk *clk);
  60
  61/* Get pll rate by id */
  62static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru,
  63                                   enum rk3368_pll_id pll_id)
  64{
  65        uint32_t nr, no, nf;
  66        uint32_t con;
  67        struct rk3368_pll *pll = &cru->pll[pll_id];
  68
  69        con = readl(&pll->con3);
  70
  71        switch ((con & PLL_MODE_MASK) >> PLL_MODE_SHIFT) {
  72        case PLL_MODE_SLOW:
  73                return OSC_HZ;
  74        case PLL_MODE_NORMAL:
  75                con = readl(&pll->con0);
  76                no = ((con & PLL_OD_MASK) >> PLL_OD_SHIFT) + 1;
  77                nr = ((con & PLL_NR_MASK) >> PLL_NR_SHIFT) + 1;
  78                con = readl(&pll->con1);
  79                nf = ((con & PLL_NF_MASK) >> PLL_NF_SHIFT) + 1;
  80
  81                return (24 * nf / (nr * no)) * 1000000;
  82        case PLL_MODE_DEEP_SLOW:
  83        default:
  84                return 32768;
  85        }
  86}
  87
  88#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
  89static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id,
  90                         const struct pll_div *div)
  91{
  92        struct rk3368_pll *pll = &cru->pll[pll_id];
  93        /* All PLLs have same VCO and output frequency range restrictions*/
  94        uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
  95        uint output_hz = vco_hz / div->no;
  96
  97        debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
  98              pll, div->nf, div->nr, div->no, vco_hz, output_hz);
  99
 100        /* enter slow mode and reset pll */
 101        rk_clrsetreg(&pll->con3, PLL_MODE_MASK | PLL_RESET_MASK,
 102                     PLL_RESET << PLL_RESET_SHIFT);
 103
 104        rk_clrsetreg(&pll->con0, PLL_NR_MASK | PLL_OD_MASK,
 105                     ((div->nr - 1) << PLL_NR_SHIFT) |
 106                     ((div->no - 1) << PLL_OD_SHIFT));
 107        writel((div->nf - 1) << PLL_NF_SHIFT, &pll->con1);
 108        /*
 109         * BWADJ should be set to NF / 2 to ensure the nominal bandwidth.
 110         * Compare the RK3368 TRM, section "3.6.4 PLL Bandwidth Adjustment".
 111         */
 112        clrsetbits_le32(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
 113
 114        udelay(10);
 115
 116        /* return from reset */
 117        rk_clrreg(&pll->con3, PLL_RESET_MASK);
 118
 119        /* waiting for pll lock */
 120        while (!(readl(&pll->con1) & PLL_LOCK_STA))
 121                udelay(1);
 122
 123        rk_clrsetreg(&pll->con3, PLL_MODE_MASK,
 124                     PLL_MODE_NORMAL << PLL_MODE_SHIFT);
 125
 126        return 0;
 127}
 128#endif
 129
 130#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
 131static void rkclk_init(struct rk3368_cru *cru)
 132{
 133        u32 apllb, aplll, dpll, cpll, gpll;
 134
 135        rkclk_set_pll(cru, APLLB, &apll_b_init_cfg);
 136        rkclk_set_pll(cru, APLLL, &apll_l_init_cfg);
 137#if !defined(CONFIG_TPL_BUILD)
 138        /*
 139         * If we plan to return to the boot ROM, we can't increase the
 140         * GPLL rate from the SPL stage.
 141         */
 142        rkclk_set_pll(cru, GPLL, &gpll_init_cfg);
 143        rkclk_set_pll(cru, CPLL, &cpll_init_cfg);
 144#endif
 145
 146        apllb = rkclk_pll_get_rate(cru, APLLB);
 147        aplll = rkclk_pll_get_rate(cru, APLLL);
 148        dpll = rkclk_pll_get_rate(cru, DPLL);
 149        cpll = rkclk_pll_get_rate(cru, CPLL);
 150        gpll = rkclk_pll_get_rate(cru, GPLL);
 151
 152        debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n",
 153               __func__, apllb, aplll, dpll, cpll, gpll);
 154}
 155#endif
 156
 157#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
 158static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id)
 159{
 160        u32 div, con, con_id, rate;
 161        u32 pll_rate;
 162
 163        switch (clk_id) {
 164        case HCLK_SDMMC:
 165                con_id = 50;
 166                break;
 167        case HCLK_EMMC:
 168                con_id = 51;
 169                break;
 170        case SCLK_SDIO0:
 171                con_id = 48;
 172                break;
 173        default:
 174                return -EINVAL;
 175        }
 176
 177        con = readl(&cru->clksel_con[con_id]);
 178        switch (con & MMC_PLL_SEL_MASK) {
 179        case MMC_PLL_SEL_GPLL:
 180                pll_rate = rkclk_pll_get_rate(cru, GPLL);
 181                break;
 182        case MMC_PLL_SEL_24M:
 183                pll_rate = OSC_HZ;
 184                break;
 185        case MMC_PLL_SEL_CPLL:
 186                pll_rate = rkclk_pll_get_rate(cru, CPLL);
 187                break;
 188        case MMC_PLL_SEL_USBPHY_480M:
 189        default:
 190                return -EINVAL;
 191        }
 192        div = (con & MMC_CLK_DIV_MASK) >> MMC_CLK_DIV_SHIFT;
 193        rate = DIV_TO_RATE(pll_rate, div);
 194
 195        debug("%s: raw rate %d (post-divide by 2)\n", __func__, rate);
 196        return rate >> 1;
 197}
 198
 199static ulong rk3368_mmc_find_best_rate_and_parent(struct clk *clk,
 200                                                  ulong rate,
 201                                                  u32 *best_mux,
 202                                                  u32 *best_div)
 203{
 204        int i;
 205        ulong best_rate = 0;
 206        const ulong MHz = 1000000;
 207        const struct {
 208                u32 mux;
 209                ulong rate;
 210        } parents[] = {
 211                { .mux = MMC_PLL_SEL_CPLL, .rate = CPLL_HZ },
 212                { .mux = MMC_PLL_SEL_GPLL, .rate = GPLL_HZ },
 213                { .mux = MMC_PLL_SEL_24M,  .rate = 24 * MHz }
 214        };
 215
 216        debug("%s: target rate %ld\n", __func__, rate);
 217        for (i = 0; i < ARRAY_SIZE(parents); ++i) {
 218                /*
 219                 * Find the largest rate no larger than the target-rate for
 220                 * the current parent.
 221                 */
 222                ulong parent_rate = parents[i].rate;
 223                u32 div = DIV_ROUND_UP(parent_rate, rate);
 224                u32 adj_div = div;
 225                ulong new_rate = parent_rate / adj_div;
 226
 227                debug("%s: rate %ld, parent-mux %d, parent-rate %ld, div %d\n",
 228                      __func__, rate, parents[i].mux, parents[i].rate, div);
 229
 230                /* Skip, if not representable */
 231                if ((div - 1) > MMC_CLK_DIV_MASK)
 232                        continue;
 233
 234                /* Skip, if we already have a better (or equal) solution */
 235                if (new_rate <= best_rate)
 236                        continue;
 237
 238                /* This is our new best rate. */
 239                best_rate = new_rate;
 240                *best_mux = parents[i].mux;
 241                *best_div = div - 1;
 242        }
 243
 244        debug("%s: best_mux = %x, best_div = %d, best_rate = %ld\n",
 245              __func__, *best_mux, *best_div, best_rate);
 246
 247        return best_rate;
 248}
 249
 250static ulong rk3368_mmc_set_clk(struct clk *clk, ulong rate)
 251{
 252        struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
 253        struct rk3368_cru *cru = priv->cru;
 254        ulong clk_id = clk->id;
 255        u32 con_id, mux = 0, div = 0;
 256
 257        /* Find the best parent and rate */
 258        rk3368_mmc_find_best_rate_and_parent(clk, rate << 1, &mux, &div);
 259
 260        switch (clk_id) {
 261        case HCLK_SDMMC:
 262                con_id = 50;
 263                break;
 264        case HCLK_EMMC:
 265                con_id = 51;
 266                break;
 267        case SCLK_SDIO0:
 268                con_id = 48;
 269                break;
 270        default:
 271                return -EINVAL;
 272        }
 273
 274        rk_clrsetreg(&cru->clksel_con[con_id],
 275                     MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK,
 276                     mux | div);
 277
 278        return rk3368_mmc_get_clk(cru, clk_id);
 279}
 280#endif
 281
 282#if IS_ENABLED(CONFIG_TPL_BUILD)
 283static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate)
 284{
 285        const struct pll_div *dpll_cfg = NULL;
 286        const ulong MHz = 1000000;
 287
 288        /* Fout = ((Fin /NR) * NF )/ NO */
 289        static const struct pll_div dpll_1200 = PLL_DIVISORS(1200 * MHz, 1, 1);
 290        static const struct pll_div dpll_1332 = PLL_DIVISORS(1332 * MHz, 2, 1);
 291        static const struct pll_div dpll_1600 = PLL_DIVISORS(1600 * MHz, 3, 2);
 292
 293        switch (set_rate) {
 294        case 1200*MHz:
 295                dpll_cfg = &dpll_1200;
 296                break;
 297        case 1332*MHz:
 298                dpll_cfg = &dpll_1332;
 299                break;
 300        case 1600*MHz:
 301                dpll_cfg = &dpll_1600;
 302                break;
 303        default:
 304                error("Unsupported SDRAM frequency!,%ld\n", set_rate);
 305        }
 306        rkclk_set_pll(cru, DPLL, dpll_cfg);
 307
 308        return set_rate;
 309}
 310#endif
 311
 312#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
 313static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru,
 314                                 ulong clk_id, ulong set_rate)
 315{
 316        /*
 317         * This models the 'assigned-clock-parents = <&ext_gmac>' from
 318         * the DTS and switches to the 'ext_gmac' clock parent.
 319         */
 320        rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
 321        return set_rate;
 322}
 323#endif
 324
 325/*
 326 * RK3368 SPI clocks have a common divider-width (7 bits) and a single bit
 327 * to select either CPLL or GPLL as the clock-parent. The location within
 328 * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable.
 329 */
 330
 331struct spi_clkreg {
 332        uint8_t reg;  /* CLKSEL_CON[reg] register in CRU */
 333        uint8_t div_shift;
 334        uint8_t sel_shift;
 335};
 336
 337/*
 338 * The entries are numbered relative to their offset from SCLK_SPI0.
 339 */
 340static const struct spi_clkreg spi_clkregs[] = {
 341        [0] = { .reg = 45, .div_shift = 0, .sel_shift = 7, },
 342        [1] = { .reg = 45, .div_shift = 8, .sel_shift = 15, },
 343        [2] = { .reg = 46, .div_shift = 8, .sel_shift = 15, },
 344};
 345
 346static inline u32 extract_bits(u32 val, unsigned width, unsigned shift)
 347{
 348        return (val >> shift) & ((1 << width) - 1);
 349}
 350
 351static ulong rk3368_spi_get_clk(struct rk3368_cru *cru, ulong clk_id)
 352{
 353        const struct spi_clkreg *spiclk = NULL;
 354        u32 div, val;
 355
 356        switch (clk_id) {
 357        case SCLK_SPI0 ... SCLK_SPI2:
 358                spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
 359                break;
 360
 361        default:
 362                error("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
 363                return -EINVAL;
 364        }
 365
 366        val = readl(&cru->clksel_con[spiclk->reg]);
 367        div = extract_bits(val, 7, spiclk->div_shift);
 368
 369        debug("%s: div 0x%x\n", __func__, div);
 370        return DIV_TO_RATE(GPLL_HZ, div);
 371}
 372
 373static ulong rk3368_spi_set_clk(struct rk3368_cru *cru, ulong clk_id, uint hz)
 374{
 375        const struct spi_clkreg *spiclk = NULL;
 376        int src_clk_div;
 377
 378        src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
 379        assert(src_clk_div < 127);
 380
 381        switch (clk_id) {
 382        case SCLK_SPI0 ... SCLK_SPI2:
 383                spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
 384                break;
 385
 386        default:
 387                error("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
 388                return -EINVAL;
 389        }
 390
 391        rk_clrsetreg(&cru->clksel_con[spiclk->reg],
 392                     ((0x7f << spiclk->div_shift) |
 393                      (0x1 << spiclk->sel_shift)),
 394                     ((src_clk_div << spiclk->div_shift) |
 395                      (1 << spiclk->sel_shift)));
 396
 397        return rk3368_spi_get_clk(cru, clk_id);
 398}
 399
 400static ulong rk3368_clk_get_rate(struct clk *clk)
 401{
 402        struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
 403        ulong rate = 0;
 404
 405        debug("%s: id %ld\n", __func__, clk->id);
 406        switch (clk->id) {
 407        case PLL_CPLL:
 408                rate = rkclk_pll_get_rate(priv->cru, CPLL);
 409                break;
 410        case PLL_GPLL:
 411                rate = rkclk_pll_get_rate(priv->cru, GPLL);
 412                break;
 413        case SCLK_SPI0 ... SCLK_SPI2:
 414                rate = rk3368_spi_get_clk(priv->cru, clk->id);
 415                break;
 416#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
 417        case HCLK_SDMMC:
 418        case HCLK_EMMC:
 419                rate = rk3368_mmc_get_clk(priv->cru, clk->id);
 420                break;
 421#endif
 422        default:
 423                return -ENOENT;
 424        }
 425
 426        return rate;
 427}
 428
 429static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
 430{
 431        __maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
 432        ulong ret = 0;
 433
 434        debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate);
 435        switch (clk->id) {
 436        case SCLK_SPI0 ... SCLK_SPI2:
 437                ret = rk3368_spi_set_clk(priv->cru, clk->id, rate);
 438                break;
 439#if IS_ENABLED(CONFIG_TPL_BUILD)
 440        case CLK_DDR:
 441                ret = rk3368_ddr_set_clk(priv->cru, rate);
 442                break;
 443#endif
 444#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
 445        case HCLK_SDMMC:
 446        case HCLK_EMMC:
 447                ret = rk3368_mmc_set_clk(clk, rate);
 448                break;
 449#endif
 450#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
 451        case SCLK_MAC:
 452                /* select the external clock */
 453                ret = rk3368_gmac_set_clk(priv->cru, clk->id, rate);
 454                break;
 455#endif
 456        default:
 457                return -ENOENT;
 458        }
 459
 460        return ret;
 461}
 462
 463static struct clk_ops rk3368_clk_ops = {
 464        .get_rate = rk3368_clk_get_rate,
 465        .set_rate = rk3368_clk_set_rate,
 466};
 467
 468static int rk3368_clk_probe(struct udevice *dev)
 469{
 470        struct rk3368_clk_priv __maybe_unused *priv = dev_get_priv(dev);
 471#if CONFIG_IS_ENABLED(OF_PLATDATA)
 472        struct rk3368_clk_plat *plat = dev_get_platdata(dev);
 473
 474        priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]);
 475#endif
 476#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
 477        rkclk_init(priv->cru);
 478#endif
 479
 480        return 0;
 481}
 482
 483static int rk3368_clk_ofdata_to_platdata(struct udevice *dev)
 484{
 485#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 486        struct rk3368_clk_priv *priv = dev_get_priv(dev);
 487
 488        priv->cru = (struct rk3368_cru *)devfdt_get_addr(dev);
 489#endif
 490
 491        return 0;
 492}
 493
 494static int rk3368_clk_bind(struct udevice *dev)
 495{
 496        int ret;
 497
 498        /* The reset driver does not have a device node, so bind it here */
 499        ret = device_bind_driver(gd->dm_root, "rk3368_sysreset", "reset", &dev);
 500        if (ret)
 501                error("bind RK3368 reset driver failed: ret=%d\n", ret);
 502
 503        return ret;
 504}
 505
 506static const struct udevice_id rk3368_clk_ids[] = {
 507        { .compatible = "rockchip,rk3368-cru" },
 508        { }
 509};
 510
 511U_BOOT_DRIVER(rockchip_rk3368_cru) = {
 512        .name           = "rockchip_rk3368_cru",
 513        .id             = UCLASS_CLK,
 514        .of_match       = rk3368_clk_ids,
 515        .priv_auto_alloc_size = sizeof(struct rk3368_clk_priv),
 516#if CONFIG_IS_ENABLED(OF_PLATDATA)
 517        .platdata_auto_alloc_size = sizeof(struct rk3368_clk_plat),
 518#endif
 519        .ofdata_to_platdata = rk3368_clk_ofdata_to_platdata,
 520        .ops            = &rk3368_clk_ops,
 521        .bind           = rk3368_clk_bind,
 522        .probe          = rk3368_clk_probe,
 523};
 524