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