uboot/drivers/clk/rockchip/clk_rk3036.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2015 Google, Inc
   3 *
   4 * SPDX-License-Identifier:     GPL-2.0
   5 */
   6
   7#include <common.h>
   8#include <clk-uclass.h>
   9#include <dm.h>
  10#include <errno.h>
  11#include <syscon.h>
  12#include <asm/io.h>
  13#include <asm/arch/clock.h>
  14#include <asm/arch/cru_rk3036.h>
  15#include <asm/arch/hardware.h>
  16#include <dm/lists.h>
  17#include <dt-bindings/clock/rk3036-cru.h>
  18#include <linux/log2.h>
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22enum {
  23        VCO_MAX_HZ      = 2400U * 1000000,
  24        VCO_MIN_HZ      = 600 * 1000000,
  25        OUTPUT_MAX_HZ   = 2400U * 1000000,
  26        OUTPUT_MIN_HZ   = 24 * 1000000,
  27};
  28
  29#define RATE_TO_DIV(input_rate, output_rate) \
  30        ((input_rate) / (output_rate) - 1);
  31
  32#define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
  33
  34#define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
  35        .refdiv = _refdiv,\
  36        .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
  37        .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
  38        _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\
  39                         OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\
  40                         #hz "Hz cannot be hit with PLL "\
  41                         "divisors on line " __stringify(__LINE__));
  42
  43/* use interge mode*/
  44static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1);
  45static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1);
  46
  47static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id,
  48                         const struct pll_div *div)
  49{
  50        int pll_id = rk_pll_id(clk_id);
  51        struct rk3036_pll *pll = &cru->pll[pll_id];
  52
  53        /* All PLLs have same VCO and output frequency range restrictions. */
  54        uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000;
  55        uint output_hz = vco_hz / div->postdiv1 / div->postdiv2;
  56
  57        debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\
  58                 vco=%u Hz, output=%u Hz\n",
  59                        pll, div->fbdiv, div->refdiv, div->postdiv1,
  60                        div->postdiv2, vco_hz, output_hz);
  61        assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
  62               output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
  63
  64        /* use interger mode */
  65        rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
  66
  67        rk_clrsetreg(&pll->con0,
  68                     PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK,
  69                     (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv);
  70        rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT |
  71                        PLL_REFDIV_MASK << PLL_REFDIV_SHIFT,
  72                        (div->postdiv2 << PLL_POSTDIV2_SHIFT |
  73                         div->refdiv << PLL_REFDIV_SHIFT));
  74
  75        /* waiting for pll lock */
  76        while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))
  77                udelay(1);
  78
  79        return 0;
  80}
  81
  82static void rkclk_init(struct rk3036_cru *cru)
  83{
  84        u32 aclk_div;
  85        u32 hclk_div;
  86        u32 pclk_div;
  87
  88        /* pll enter slow-mode */
  89        rk_clrsetreg(&cru->cru_mode_con,
  90                     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
  91                     APLL_MODE_MASK << APLL_MODE_SHIFT,
  92                     GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
  93                     APLL_MODE_SLOW << APLL_MODE_SHIFT);
  94
  95        /* init pll */
  96        rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
  97        rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
  98
  99        /*
 100         * select apll as core clock pll source and
 101         * set up dependent divisors for PCLK/HCLK and ACLK clocks.
 102         * core hz : apll = 1:1
 103         */
 104        aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
 105        assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7);
 106
 107        pclk_div = APLL_HZ / CORE_PERI_HZ - 1;
 108        assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf);
 109
 110        rk_clrsetreg(&cru->cru_clksel_con[0],
 111                     CORE_CLK_PLL_SEL_MASK << CORE_CLK_PLL_SEL_SHIFT |
 112                     CORE_DIV_CON_MASK << CORE_DIV_CON_SHIFT,
 113                     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
 114                     0 << CORE_DIV_CON_SHIFT);
 115
 116        rk_clrsetreg(&cru->cru_clksel_con[1],
 117                     CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT |
 118                     CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT,
 119                     aclk_div << CORE_ACLK_DIV_SHIFT |
 120                     pclk_div << CORE_PERI_DIV_SHIFT);
 121
 122        /*
 123         * select apll as cpu clock pll source and
 124         * set up dependent divisors for PCLK/HCLK and ACLK clocks.
 125         */
 126        aclk_div = APLL_HZ / CPU_ACLK_HZ - 1;
 127        assert((aclk_div + 1) * CPU_ACLK_HZ == APLL_HZ && aclk_div < 0x1f);
 128
 129        pclk_div = APLL_HZ / CPU_PCLK_HZ - 1;
 130        assert((pclk_div + 1) * CPU_PCLK_HZ == APLL_HZ && pclk_div < 0x7);
 131
 132        hclk_div = APLL_HZ / CPU_HCLK_HZ - 1;
 133        assert((hclk_div + 1) * CPU_HCLK_HZ == APLL_HZ && hclk_div < 0x3);
 134
 135        rk_clrsetreg(&cru->cru_clksel_con[0],
 136                     CPU_CLK_PLL_SEL_MASK << CPU_CLK_PLL_SEL_SHIFT |
 137                     ACLK_CPU_DIV_MASK << ACLK_CPU_DIV_SHIFT,
 138                     CPU_CLK_PLL_SEL_APLL << CPU_CLK_PLL_SEL_SHIFT |
 139                     aclk_div << ACLK_CPU_DIV_SHIFT);
 140
 141        rk_clrsetreg(&cru->cru_clksel_con[1],
 142                     CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
 143                     CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
 144                     pclk_div << CPU_PCLK_DIV_SHIFT |
 145                     hclk_div << CPU_HCLK_DIV_SHIFT);
 146
 147        /*
 148         * select gpll as peri clock pll source and
 149         * set up dependent divisors for PCLK/HCLK and ACLK clocks.
 150         */
 151        aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
 152        assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
 153
 154        hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
 155        assert((1 << hclk_div) * PERI_HCLK_HZ ==
 156                PERI_ACLK_HZ && (pclk_div < 0x4));
 157
 158        pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
 159        assert((1 << pclk_div) * PERI_PCLK_HZ ==
 160                PERI_ACLK_HZ && pclk_div < 0x8);
 161
 162        rk_clrsetreg(&cru->cru_clksel_con[10],
 163                     PERI_PLL_SEL_MASK << PERI_PLL_SEL_SHIFT |
 164                     PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
 165                     PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
 166                     PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
 167                     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
 168                     pclk_div << PERI_PCLK_DIV_SHIFT |
 169                     hclk_div << PERI_HCLK_DIV_SHIFT |
 170                     aclk_div << PERI_ACLK_DIV_SHIFT);
 171
 172        /* PLL enter normal-mode */
 173        rk_clrsetreg(&cru->cru_mode_con,
 174                     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
 175                     APLL_MODE_MASK << APLL_MODE_SHIFT,
 176                     GPLL_MODE_NORM << GPLL_MODE_SHIFT |
 177                     APLL_MODE_NORM << APLL_MODE_SHIFT);
 178}
 179
 180/* Get pll rate by id */
 181static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru,
 182                                   enum rk_clk_id clk_id)
 183{
 184        uint32_t refdiv, fbdiv, postdiv1, postdiv2;
 185        uint32_t con;
 186        int pll_id = rk_pll_id(clk_id);
 187        struct rk3036_pll *pll = &cru->pll[pll_id];
 188        static u8 clk_shift[CLK_COUNT] = {
 189                0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff,
 190                GPLL_MODE_SHIFT, 0xff
 191        };
 192        static u8 clk_mask[CLK_COUNT] = {
 193                0xff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xff,
 194                GPLL_MODE_MASK, 0xff
 195        };
 196        uint shift;
 197        uint mask;
 198
 199        con = readl(&cru->cru_mode_con);
 200        shift = clk_shift[clk_id];
 201        mask = clk_mask[clk_id];
 202
 203        switch ((con >> shift) & mask) {
 204        case GPLL_MODE_SLOW:
 205                return OSC_HZ;
 206        case GPLL_MODE_NORM:
 207
 208                /* normal mode */
 209                con = readl(&pll->con0);
 210                postdiv1 = (con >> PLL_POSTDIV1_SHIFT) & PLL_POSTDIV1_MASK;
 211                fbdiv = (con >> PLL_FBDIV_SHIFT) & PLL_FBDIV_MASK;
 212                con = readl(&pll->con1);
 213                postdiv2 = (con >> PLL_POSTDIV2_SHIFT) & PLL_POSTDIV2_MASK;
 214                refdiv = (con >> PLL_REFDIV_SHIFT) & PLL_REFDIV_MASK;
 215                return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
 216        case GPLL_MODE_DEEP:
 217        default:
 218                return 32768;
 219        }
 220}
 221
 222static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate,
 223                                  int periph)
 224{
 225        uint src_rate;
 226        uint div, mux;
 227        u32 con;
 228
 229        switch (periph) {
 230        case HCLK_EMMC:
 231                con = readl(&cru->cru_clksel_con[12]);
 232                mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
 233                div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
 234                break;
 235        case HCLK_SDIO:
 236                con = readl(&cru->cru_clksel_con[12]);
 237                mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK;
 238                div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
 239                break;
 240        default:
 241                return -EINVAL;
 242        }
 243
 244        src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate;
 245        return DIV_TO_RATE(src_rate, div);
 246}
 247
 248static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate,
 249                                  int periph, uint freq)
 250{
 251        int src_clk_div;
 252        int mux;
 253
 254        debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
 255
 256        /* mmc clock auto divide 2 in internal */
 257        src_clk_div = (clk_general_rate / 2 + freq - 1) / freq;
 258
 259        if (src_clk_div > 0x7f) {
 260                src_clk_div = (OSC_HZ / 2 + freq - 1) / freq;
 261                mux = EMMC_SEL_24M;
 262        } else {
 263                mux = EMMC_SEL_GPLL;
 264        }
 265
 266        switch (periph) {
 267        case HCLK_EMMC:
 268                rk_clrsetreg(&cru->cru_clksel_con[12],
 269                             EMMC_PLL_MASK << EMMC_PLL_SHIFT |
 270                             EMMC_DIV_MASK << EMMC_DIV_SHIFT,
 271                             mux << EMMC_PLL_SHIFT |
 272                             (src_clk_div - 1) << EMMC_DIV_SHIFT);
 273                break;
 274        case HCLK_SDIO:
 275                rk_clrsetreg(&cru->cru_clksel_con[11],
 276                             MMC0_PLL_MASK << MMC0_PLL_SHIFT |
 277                             MMC0_DIV_MASK << MMC0_DIV_SHIFT,
 278                             mux << MMC0_PLL_SHIFT |
 279                             (src_clk_div - 1) << MMC0_DIV_SHIFT);
 280                break;
 281        default:
 282                return -EINVAL;
 283        }
 284
 285        return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
 286}
 287
 288static ulong rk3036_clk_get_rate(struct clk *clk)
 289{
 290        struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
 291
 292        switch (clk->id) {
 293        case 0 ... 63:
 294                return rkclk_pll_get_rate(priv->cru, clk->id);
 295        default:
 296                return -ENOENT;
 297        }
 298}
 299
 300static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate)
 301{
 302        struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
 303        ulong new_rate, gclk_rate;
 304
 305        gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
 306        switch (clk->id) {
 307        case 0 ... 63:
 308                return 0;
 309        case HCLK_EMMC:
 310                new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate,
 311                                                clk->id, rate);
 312                break;
 313        default:
 314                return -ENOENT;
 315        }
 316
 317        return new_rate;
 318}
 319
 320static struct clk_ops rk3036_clk_ops = {
 321        .get_rate       = rk3036_clk_get_rate,
 322        .set_rate       = rk3036_clk_set_rate,
 323};
 324
 325static int rk3036_clk_probe(struct udevice *dev)
 326{
 327        struct rk3036_clk_priv *priv = dev_get_priv(dev);
 328
 329        priv->cru = (struct rk3036_cru *)dev_get_addr(dev);
 330        rkclk_init(priv->cru);
 331
 332        return 0;
 333}
 334
 335static int rk3036_clk_bind(struct udevice *dev)
 336{
 337        int ret;
 338
 339        /* The reset driver does not have a device node, so bind it here */
 340        ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev);
 341        if (ret)
 342                debug("Warning: No RK3036 reset driver: ret=%d\n", ret);
 343
 344        return 0;
 345}
 346
 347static const struct udevice_id rk3036_clk_ids[] = {
 348        { .compatible = "rockchip,rk3036-cru" },
 349        { }
 350};
 351
 352U_BOOT_DRIVER(rockchip_rk3036_cru) = {
 353        .name           = "clk_rk3036",
 354        .id             = UCLASS_CLK,
 355        .of_match       = rk3036_clk_ids,
 356        .priv_auto_alloc_size = sizeof(struct rk3036_clk_priv),
 357        .ops            = &rk3036_clk_ops,
 358        .bind           = rk3036_clk_bind,
 359        .probe          = rk3036_clk_probe,
 360};
 361