uboot/drivers/clk/rockchip/clk_pll.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * (C) Copyright 2018-2019 Rockchip Electronics Co., Ltd
   4 */
   5 #include <common.h>
   6#include <bitfield.h>
   7#include <clk-uclass.h>
   8#include <dm.h>
   9#include <errno.h>
  10#include <log.h>
  11#include <asm/io.h>
  12#include <asm/arch-rockchip/clock.h>
  13#include <asm/arch-rockchip/hardware.h>
  14#include <div64.h>
  15#include <linux/delay.h>
  16
  17static struct rockchip_pll_rate_table rockchip_auto_table;
  18
  19#define PLL_MODE_MASK                           0x3
  20#define PLL_RK3328_MODE_MASK                    0x1
  21
  22#define RK3036_PLLCON0_FBDIV_MASK               0xfff
  23#define RK3036_PLLCON0_FBDIV_SHIFT              0
  24#define RK3036_PLLCON0_POSTDIV1_MASK            0x7 << 12
  25#define RK3036_PLLCON0_POSTDIV1_SHIFT           12
  26#define RK3036_PLLCON1_REFDIV_MASK              0x3f
  27#define RK3036_PLLCON1_REFDIV_SHIFT             0
  28#define RK3036_PLLCON1_POSTDIV2_MASK            0x7 << 6
  29#define RK3036_PLLCON1_POSTDIV2_SHIFT           6
  30#define RK3036_PLLCON1_DSMPD_MASK               0x1 << 12
  31#define RK3036_PLLCON1_DSMPD_SHIFT              12
  32#define RK3036_PLLCON2_FRAC_MASK                0xffffff
  33#define RK3036_PLLCON2_FRAC_SHIFT               0
  34#define RK3036_PLLCON1_PWRDOWN_SHIFT            13
  35
  36#define MHZ             1000000
  37#define KHZ             1000
  38enum {
  39        OSC_HZ                  = 24 * 1000000,
  40        VCO_MAX_HZ      = 3200U * 1000000,
  41        VCO_MIN_HZ      = 800 * 1000000,
  42        OUTPUT_MAX_HZ   = 3200U * 1000000,
  43        OUTPUT_MIN_HZ   = 24 * 1000000,
  44};
  45
  46#define MIN_FOUTVCO_FREQ        (800 * MHZ)
  47#define MAX_FOUTVCO_FREQ        (2000 * MHZ)
  48#define RK3588_VCO_MIN_HZ       (2250UL * MHZ)
  49#define RK3588_VCO_MAX_HZ       (4500UL * MHZ)
  50#define RK3588_FOUT_MIN_HZ      (37UL * MHZ)
  51#define RK3588_FOUT_MAX_HZ      (4500UL * MHZ)
  52
  53int gcd(int m, int n)
  54{
  55        int t;
  56
  57        while (m > 0) {
  58                if (n > m) {
  59                        t = m;
  60                        m = n;
  61                        n = t;
  62                } /* swap */
  63                m -= n;
  64        }
  65        return n;
  66}
  67
  68/*
  69 * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
  70 * Formulas also embedded within the Fractional PLL Verilog model:
  71 * If DSMPD = 1 (DSM is disabled, "integer mode")
  72 * FOUTVCO = FREF / REFDIV * FBDIV
  73 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
  74 * Where:
  75 * FOUTVCO = Fractional PLL non-divided output frequency
  76 * FOUTPOSTDIV = Fractional PLL divided output frequency
  77 *               (output of second post divider)
  78 * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
  79 * REFDIV = Fractional PLL input reference clock divider
  80 * FBDIV = Integer value programmed into feedback divide
  81 *
  82 */
  83
  84static int rockchip_pll_clk_set_postdiv(ulong fout_hz,
  85                                        u32 *postdiv1,
  86                                        u32 *postdiv2,
  87                                        u32 *foutvco)
  88{
  89        ulong freq;
  90
  91        if (fout_hz < MIN_FOUTVCO_FREQ) {
  92                for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
  93                        for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
  94                                freq = fout_hz * (*postdiv1) * (*postdiv2);
  95                                if (freq >= MIN_FOUTVCO_FREQ &&
  96                                    freq <= MAX_FOUTVCO_FREQ) {
  97                                        *foutvco = freq;
  98                                        return 0;
  99                                }
 100                        }
 101                }
 102                printf("Can't FIND postdiv1/2 to make fout=%lu in 800~2000M.\n",
 103                       fout_hz);
 104        } else {
 105                *postdiv1 = 1;
 106                *postdiv2 = 1;
 107        }
 108        return 0;
 109}
 110
 111static struct rockchip_pll_rate_table *
 112rockchip_pll_clk_set_by_auto(ulong fin_hz,
 113                             ulong fout_hz)
 114{
 115        struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
 116        /* FIXME set postdiv1/2 always 1*/
 117        u32 foutvco = fout_hz;
 118        ulong fin_64, frac_64;
 119        u32 f_frac, postdiv1, postdiv2;
 120        ulong clk_gcd = 0;
 121
 122        if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
 123                return NULL;
 124
 125        rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco);
 126        rate_table->postdiv1 = postdiv1;
 127        rate_table->postdiv2 = postdiv2;
 128        rate_table->dsmpd = 1;
 129
 130        if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
 131                fin_hz /= MHZ;
 132                foutvco /= MHZ;
 133                clk_gcd = gcd(fin_hz, foutvco);
 134                rate_table->refdiv = fin_hz / clk_gcd;
 135                rate_table->fbdiv = foutvco / clk_gcd;
 136
 137                rate_table->frac = 0;
 138
 139                debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n",
 140                      fin_hz, fout_hz, clk_gcd);
 141                debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n",
 142                      rate_table->refdiv,
 143                      rate_table->fbdiv, rate_table->postdiv1,
 144                      rate_table->postdiv2);
 145        } else {
 146                debug("frac div,fin_hz = %ld,fout_hz = %ld\n",
 147                      fin_hz, fout_hz);
 148                debug("frac get postdiv1 = %d,  postdiv2 = %d, foutvco = %d\n",
 149                      rate_table->postdiv1, rate_table->postdiv2, foutvco);
 150                clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
 151                rate_table->refdiv = fin_hz / MHZ / clk_gcd;
 152                rate_table->fbdiv = foutvco / MHZ / clk_gcd;
 153                debug("frac get refdiv = %d,  fbdiv = %d\n",
 154                      rate_table->refdiv, rate_table->fbdiv);
 155
 156                rate_table->frac = 0;
 157
 158                f_frac = (foutvco % MHZ);
 159                fin_64 = fin_hz;
 160                fin_64 = fin_64 / rate_table->refdiv;
 161                frac_64 = f_frac << 24;
 162                frac_64 = frac_64 / fin_64;
 163                rate_table->frac = frac_64;
 164                if (rate_table->frac > 0)
 165                        rate_table->dsmpd = 0;
 166                debug("frac = %x\n", rate_table->frac);
 167        }
 168        return rate_table;
 169}
 170
 171static struct rockchip_pll_rate_table *
 172rk3588_pll_clk_set_by_auto(unsigned long fin_hz,
 173                           unsigned long fout_hz)
 174{
 175        struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
 176        u32 p, m, s;
 177        ulong fvco, fref, fout, ffrac;
 178
 179        if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
 180                return NULL;
 181
 182        if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ)
 183                return NULL;
 184
 185        if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
 186                for (s = 0; s <= 6; s++) {
 187                        fvco = fout_hz << s;
 188                        if (fvco < RK3588_VCO_MIN_HZ ||
 189                            fvco > RK3588_VCO_MAX_HZ)
 190                                continue;
 191                        for (p = 2; p <= 4; p++) {
 192                                for (m = 64; m <= 1023; m++) {
 193                                        if (fvco == m * fin_hz / p) {
 194                                                rate_table->p = p;
 195                                                rate_table->m = m;
 196                                                rate_table->s = s;
 197                                                rate_table->k = 0;
 198                                                return rate_table;
 199                                        }
 200                                }
 201                        }
 202                }
 203                pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
 204        } else {
 205                for (s = 0; s <= 6; s++) {
 206                        fvco = fout_hz << s;
 207                        if (fvco < RK3588_VCO_MIN_HZ ||
 208                            fvco > RK3588_VCO_MAX_HZ)
 209                                continue;
 210                        for (p = 1; p <= 4; p++) {
 211                                for (m = 64; m <= 1023; m++) {
 212                                        if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) {
 213                                                rate_table->p = p;
 214                                                rate_table->m = m;
 215                                                rate_table->s = s;
 216                                                fref = fin_hz / p;
 217                                                ffrac = fvco - (m * fref);
 218                                                fout = ffrac * 65536;
 219                                                rate_table->k = fout / fref;
 220                                                return rate_table;
 221                                        }
 222                                }
 223                        }
 224                }
 225                pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
 226        }
 227        return NULL;
 228}
 229
 230static const struct rockchip_pll_rate_table *
 231rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate)
 232{
 233        struct rockchip_pll_rate_table  *rate_table = pll->rate_table;
 234
 235        while (rate_table->rate) {
 236                if (rate_table->rate == rate)
 237                        break;
 238                rate_table++;
 239        }
 240        if (rate_table->rate != rate) {
 241                if (pll->type == pll_rk3588)
 242                        return rk3588_pll_clk_set_by_auto(24 * MHZ, rate);
 243                else
 244                        return rockchip_pll_clk_set_by_auto(24 * MHZ, rate);
 245        } else {
 246                return rate_table;
 247        }
 248}
 249
 250static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
 251                               void __iomem *base, ulong pll_id,
 252                               ulong drate)
 253{
 254        const struct rockchip_pll_rate_table *rate;
 255
 256        rate = rockchip_get_pll_settings(pll, drate);
 257        if (!rate) {
 258                printf("%s unsupport rate\n", __func__);
 259                return -EINVAL;
 260        }
 261
 262        debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d\n",
 263              __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv);
 264        debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: %d\n",
 265              __func__, rate->rate, rate->postdiv2, rate->dsmpd, rate->frac);
 266
 267        /*
 268         * When power on or changing PLL setting,
 269         * we must force PLL into slow mode to ensure output stable clock.
 270         */
 271        rk_clrsetreg(base + pll->mode_offset,
 272                     pll->mode_mask << pll->mode_shift,
 273                     RKCLK_PLL_MODE_SLOW << pll->mode_shift);
 274
 275        /* Power down */
 276        rk_setreg(base + pll->con_offset + 0x4,
 277                  1 << RK3036_PLLCON1_PWRDOWN_SHIFT);
 278
 279        rk_clrsetreg(base + pll->con_offset,
 280                     (RK3036_PLLCON0_POSTDIV1_MASK |
 281                     RK3036_PLLCON0_FBDIV_MASK),
 282                     (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) |
 283                     rate->fbdiv);
 284        rk_clrsetreg(base + pll->con_offset + 0x4,
 285                     (RK3036_PLLCON1_POSTDIV2_MASK |
 286                     RK3036_PLLCON1_REFDIV_MASK),
 287                     (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT |
 288                     rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT));
 289        if (!rate->dsmpd) {
 290                rk_clrsetreg(base + pll->con_offset + 0x4,
 291                             RK3036_PLLCON1_DSMPD_MASK,
 292                             rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT);
 293                writel((readl(base + pll->con_offset + 0x8) &
 294                        (~RK3036_PLLCON2_FRAC_MASK)) |
 295                            (rate->frac << RK3036_PLLCON2_FRAC_SHIFT),
 296                            base + pll->con_offset + 0x8);
 297        }
 298
 299        /* Power Up */
 300        rk_clrreg(base + pll->con_offset + 0x4,
 301                  1 << RK3036_PLLCON1_PWRDOWN_SHIFT);
 302
 303        /* waiting for pll lock */
 304        while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift)))
 305                udelay(1);
 306
 307        rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
 308                     RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
 309        debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
 310              pll, readl(base + pll->con_offset),
 311              readl(base + pll->con_offset + 0x4),
 312              readl(base + pll->con_offset + 0x8),
 313              readl(base + pll->mode_offset));
 314
 315        return 0;
 316}
 317
 318static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll,
 319                                 void __iomem *base, ulong pll_id)
 320{
 321        u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac;
 322        u32 con = 0, shift, mask;
 323        ulong rate;
 324
 325        con = readl(base + pll->mode_offset);
 326        shift = pll->mode_shift;
 327        mask = pll->mode_mask << shift;
 328
 329        switch ((con & mask) >> shift) {
 330        case RKCLK_PLL_MODE_SLOW:
 331                return OSC_HZ;
 332        case RKCLK_PLL_MODE_NORMAL:
 333                /* normal mode */
 334                con = readl(base + pll->con_offset);
 335                postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >>
 336                           RK3036_PLLCON0_POSTDIV1_SHIFT;
 337                fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >>
 338                        RK3036_PLLCON0_FBDIV_SHIFT;
 339                con = readl(base + pll->con_offset + 0x4);
 340                postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >>
 341                           RK3036_PLLCON1_POSTDIV2_SHIFT;
 342                refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >>
 343                         RK3036_PLLCON1_REFDIV_SHIFT;
 344                dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >>
 345                        RK3036_PLLCON1_DSMPD_SHIFT;
 346                con = readl(base + pll->con_offset + 0x8);
 347                frac = (con & RK3036_PLLCON2_FRAC_MASK) >>
 348                        RK3036_PLLCON2_FRAC_SHIFT;
 349                rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
 350                if (dsmpd == 0) {
 351                        u64 frac_rate = OSC_HZ * (u64)frac;
 352
 353                        do_div(frac_rate, refdiv);
 354                        frac_rate >>= 24;
 355                        do_div(frac_rate, postdiv1);
 356                        do_div(frac_rate, postdiv1);
 357                        rate += frac_rate;
 358                }
 359                return rate;
 360        case RKCLK_PLL_MODE_DEEP:
 361        default:
 362                return 32768;
 363        }
 364}
 365
 366#define RK3588_PLLCON(i)                ((i) * 0x4)
 367#define RK3588_PLLCON0_M_MASK           0x3ff << 0
 368#define RK3588_PLLCON0_M_SHIFT          0
 369#define RK3588_PLLCON1_P_MASK           0x3f << 0
 370#define RK3588_PLLCON1_P_SHIFT          0
 371#define RK3588_PLLCON1_S_MASK           0x7 << 6
 372#define RK3588_PLLCON1_S_SHIFT          6
 373#define RK3588_PLLCON2_K_MASK           0xffff
 374#define RK3588_PLLCON2_K_SHIFT          0
 375#define RK3588_PLLCON1_PWRDOWN          BIT(13)
 376#define RK3588_PLLCON6_LOCK_STATUS      BIT(15)
 377#define RK3588_B0PLL_CLKSEL_CON(i)      ((i) * 0x4 + 0x50000 + 0x300)
 378#define RK3588_B1PLL_CLKSEL_CON(i)      ((i) * 0x4 + 0x52000 + 0x300)
 379#define RK3588_LPLL_CLKSEL_CON(i)       ((i) * 0x4 + 0x58000 + 0x300)
 380#define RK3588_CORE_DIV_MASK            0x1f
 381#define RK3588_CORE_L02_DIV_SHIFT       0
 382#define RK3588_CORE_L13_DIV_SHIFT       7
 383#define RK3588_CORE_B02_DIV_SHIFT       8
 384#define RK3588_CORE_B13_DIV_SHIFT       0
 385
 386static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll,
 387                               void __iomem *base, ulong pll_id,
 388                               ulong drate)
 389{
 390        const struct rockchip_pll_rate_table *rate;
 391
 392        rate = rockchip_get_pll_settings(pll, drate);
 393        if (!rate) {
 394                printf("%s unsupported rate\n", __func__);
 395                return -EINVAL;
 396        }
 397
 398        debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
 399              __func__, rate->rate, rate->p, rate->m, rate->s, rate->k);
 400
 401        /*
 402         * When power on or changing PLL setting,
 403         * we must force PLL into slow mode to ensure output stable clock.
 404         */
 405        if (pll_id == 3)
 406                rk_clrsetreg(base + 0x84c, 0x1 << 1, 0x1 << 1);
 407
 408        rk_clrsetreg(base + pll->mode_offset,
 409                     pll->mode_mask << pll->mode_shift,
 410                     RKCLK_PLL_MODE_SLOW << pll->mode_shift);
 411        if (pll_id == 0)
 412                rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
 413                             pll->mode_mask << 6,
 414                             RKCLK_PLL_MODE_SLOW << 6);
 415        else if (pll_id == 1)
 416                rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
 417                             pll->mode_mask << 6,
 418                             RKCLK_PLL_MODE_SLOW << 6);
 419        else if (pll_id == 2)
 420                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5),
 421                             pll->mode_mask << 14,
 422                             RKCLK_PLL_MODE_SLOW << 14);
 423
 424        /* Power down */
 425        rk_setreg(base + pll->con_offset + RK3588_PLLCON(1),
 426                  RK3588_PLLCON1_PWRDOWN);
 427
 428        rk_clrsetreg(base + pll->con_offset,
 429                     RK3588_PLLCON0_M_MASK,
 430                     (rate->m << RK3588_PLLCON0_M_SHIFT));
 431        rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1),
 432                     (RK3588_PLLCON1_P_MASK |
 433                     RK3588_PLLCON1_S_MASK),
 434                     (rate->p << RK3588_PLLCON1_P_SHIFT |
 435                     rate->s << RK3588_PLLCON1_S_SHIFT));
 436        if (rate->k) {
 437                rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2),
 438                             RK3588_PLLCON2_K_MASK,
 439                             rate->k << RK3588_PLLCON2_K_SHIFT);
 440        }
 441        /* Power up */
 442        rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1),
 443                  RK3588_PLLCON1_PWRDOWN);
 444
 445        /* waiting for pll lock */
 446        while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) &
 447                RK3588_PLLCON6_LOCK_STATUS)) {
 448                udelay(1);
 449                debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id);
 450        }
 451
 452        rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
 453                     RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
 454        if (pll_id == 0) {
 455                rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
 456                             pll->mode_mask << 6,
 457                             2 << 6);
 458                rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
 459                             RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT,
 460                             0 << RK3588_CORE_B02_DIV_SHIFT);
 461                rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(1),
 462                             RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT,
 463                             0 << RK3588_CORE_B13_DIV_SHIFT);
 464        } else if (pll_id == 1) {
 465                rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
 466                             pll->mode_mask << 6,
 467                             2 << 6);
 468                rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
 469                             RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT,
 470                             0 << RK3588_CORE_B02_DIV_SHIFT);
 471                rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(1),
 472                             RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT,
 473                             0 << RK3588_CORE_B13_DIV_SHIFT);
 474        } else if (pll_id == 2) {
 475                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5),
 476                             pll->mode_mask << 14,
 477                             2 << 14);
 478                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6),
 479                             RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT,
 480                             0 << RK3588_CORE_L13_DIV_SHIFT);
 481                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6),
 482                             RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT,
 483                             0 << RK3588_CORE_L02_DIV_SHIFT);
 484                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7),
 485                             RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT,
 486                             0 << RK3588_CORE_L13_DIV_SHIFT);
 487                rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7),
 488                             RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT,
 489                             0 << RK3588_CORE_L02_DIV_SHIFT);
 490        }
 491
 492        if (pll_id == 3)
 493                rk_clrsetreg(base + 0x84c, 0x1 << 1, 0);
 494
 495        debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
 496              pll, readl(base + pll->con_offset),
 497              readl(base + pll->con_offset + 0x4),
 498              readl(base + pll->con_offset + 0x8),
 499              readl(base + pll->mode_offset));
 500
 501        return 0;
 502}
 503
 504static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll,
 505                                 void __iomem *base, ulong pll_id)
 506{
 507        u32 m, p, s, k;
 508        u32 con = 0, shift, mode;
 509        u64 rate, postdiv;
 510
 511        con = readl(base + pll->mode_offset);
 512        shift = pll->mode_shift;
 513        if (pll_id == 8)
 514                mode = RKCLK_PLL_MODE_NORMAL;
 515        else
 516                mode = (con & (pll->mode_mask << shift)) >> shift;
 517        switch (mode) {
 518        case RKCLK_PLL_MODE_SLOW:
 519                return OSC_HZ;
 520        case RKCLK_PLL_MODE_NORMAL:
 521                /* normal mode */
 522                con = readl(base + pll->con_offset);
 523                m = (con & RK3588_PLLCON0_M_MASK) >>
 524                           RK3588_PLLCON0_M_SHIFT;
 525                con = readl(base + pll->con_offset + RK3588_PLLCON(1));
 526                p = (con & RK3588_PLLCON1_P_MASK) >>
 527                           RK3036_PLLCON0_FBDIV_SHIFT;
 528                s = (con & RK3588_PLLCON1_S_MASK) >>
 529                         RK3588_PLLCON1_S_SHIFT;
 530                con = readl(base + pll->con_offset + RK3588_PLLCON(2));
 531                k = (con & RK3588_PLLCON2_K_MASK) >>
 532                        RK3588_PLLCON2_K_SHIFT;
 533
 534                rate = OSC_HZ / p;
 535                rate *= m;
 536                if (k) {
 537                        /* fractional mode */
 538                        u64 frac_rate64 = OSC_HZ * k;
 539
 540                        postdiv = p * 65536;
 541                        do_div(frac_rate64, postdiv);
 542                        rate += frac_rate64;
 543                }
 544                rate = rate >> s;
 545                return rate;
 546        case RKCLK_PLL_MODE_DEEP:
 547        default:
 548                return 32768;
 549        }
 550}
 551
 552ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll,
 553                            void __iomem *base,
 554                            ulong pll_id)
 555{
 556        ulong rate = 0;
 557
 558        switch (pll->type) {
 559        case pll_rk3036:
 560                pll->mode_mask = PLL_MODE_MASK;
 561                rate = rk3036_pll_get_rate(pll, base, pll_id);
 562                break;
 563        case pll_rk3328:
 564                pll->mode_mask = PLL_RK3328_MODE_MASK;
 565                rate = rk3036_pll_get_rate(pll, base, pll_id);
 566                break;
 567        case pll_rk3588:
 568                pll->mode_mask = PLL_MODE_MASK;
 569                rate = rk3588_pll_get_rate(pll, base, pll_id);
 570                break;
 571        default:
 572                printf("%s: Unknown pll type for pll clk %ld\n",
 573                       __func__, pll_id);
 574        }
 575        return rate;
 576}
 577
 578int rockchip_pll_set_rate(struct rockchip_pll_clock *pll,
 579                          void __iomem *base, ulong pll_id,
 580                          ulong drate)
 581{
 582        int ret = 0;
 583
 584        if (rockchip_pll_get_rate(pll, base, pll_id) == drate)
 585                return 0;
 586
 587        switch (pll->type) {
 588        case pll_rk3036:
 589                pll->mode_mask = PLL_MODE_MASK;
 590                ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
 591                break;
 592        case pll_rk3328:
 593                pll->mode_mask = PLL_RK3328_MODE_MASK;
 594                ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
 595                break;
 596        case pll_rk3588:
 597                pll->mode_mask = PLL_MODE_MASK;
 598                ret = rk3588_pll_set_rate(pll, base, pll_id, drate);
 599                break;
 600        default:
 601                printf("%s: Unknown pll type for pll clk %ld\n",
 602                       __func__, pll_id);
 603        }
 604        return ret;
 605}
 606
 607const struct rockchip_cpu_rate_table *
 608rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table,
 609                          ulong rate)
 610{
 611        struct rockchip_cpu_rate_table *ps = cpu_table;
 612
 613        while (ps->rate) {
 614                if (ps->rate == rate)
 615                        break;
 616                ps++;
 617        }
 618        if (ps->rate != rate)
 619                return NULL;
 620        else
 621                return ps;
 622}
 623