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 <asm/io.h>
  11#include <asm/arch-rockchip/clock.h>
  12#include <asm/arch-rockchip/hardware.h>
  13#include <div64.h>
  14
  15static struct rockchip_pll_rate_table rockchip_auto_table;
  16
  17#define PLL_MODE_MASK                           0x3
  18#define PLL_RK3328_MODE_MASK                    0x1
  19
  20#define RK3036_PLLCON0_FBDIV_MASK               0xfff
  21#define RK3036_PLLCON0_FBDIV_SHIFT              0
  22#define RK3036_PLLCON0_POSTDIV1_MASK            0x7 << 12
  23#define RK3036_PLLCON0_POSTDIV1_SHIFT           12
  24#define RK3036_PLLCON1_REFDIV_MASK              0x3f
  25#define RK3036_PLLCON1_REFDIV_SHIFT             0
  26#define RK3036_PLLCON1_POSTDIV2_MASK            0x7 << 6
  27#define RK3036_PLLCON1_POSTDIV2_SHIFT           6
  28#define RK3036_PLLCON1_DSMPD_MASK               0x1 << 12
  29#define RK3036_PLLCON1_DSMPD_SHIFT              12
  30#define RK3036_PLLCON2_FRAC_MASK                0xffffff
  31#define RK3036_PLLCON2_FRAC_SHIFT               0
  32#define RK3036_PLLCON1_PWRDOWN_SHIT             13
  33
  34#define MHZ             1000000
  35#define KHZ             1000
  36enum {
  37        OSC_HZ                  = 24 * 1000000,
  38        VCO_MAX_HZ      = 3200U * 1000000,
  39        VCO_MIN_HZ      = 800 * 1000000,
  40        OUTPUT_MAX_HZ   = 3200U * 1000000,
  41        OUTPUT_MIN_HZ   = 24 * 1000000,
  42};
  43
  44#define MIN_FOUTVCO_FREQ        (800 * MHZ)
  45#define MAX_FOUTVCO_FREQ        (2000 * MHZ)
  46
  47int gcd(int m, int n)
  48{
  49        int t;
  50
  51        while (m > 0) {
  52                if (n > m) {
  53                        t = m;
  54                        m = n;
  55                        n = t;
  56                } /* swap */
  57                m -= n;
  58        }
  59        return n;
  60}
  61
  62/*
  63 * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
  64 * Formulas also embedded within the Fractional PLL Verilog model:
  65 * If DSMPD = 1 (DSM is disabled, "integer mode")
  66 * FOUTVCO = FREF / REFDIV * FBDIV
  67 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
  68 * Where:
  69 * FOUTVCO = Fractional PLL non-divided output frequency
  70 * FOUTPOSTDIV = Fractional PLL divided output frequency
  71 *               (output of second post divider)
  72 * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
  73 * REFDIV = Fractional PLL input reference clock divider
  74 * FBDIV = Integer value programmed into feedback divide
  75 *
  76 */
  77
  78static int rockchip_pll_clk_set_postdiv(ulong fout_hz,
  79                                        u32 *postdiv1,
  80                                        u32 *postdiv2,
  81                                        u32 *foutvco)
  82{
  83        ulong freq;
  84
  85        if (fout_hz < MIN_FOUTVCO_FREQ) {
  86                for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
  87                        for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
  88                                freq = fout_hz * (*postdiv1) * (*postdiv2);
  89                                if (freq >= MIN_FOUTVCO_FREQ &&
  90                                    freq <= MAX_FOUTVCO_FREQ) {
  91                                        *foutvco = freq;
  92                                        return 0;
  93                                }
  94                        }
  95                }
  96                printf("Can't FIND postdiv1/2 to make fout=%lu in 800~2000M.\n",
  97                       fout_hz);
  98        } else {
  99                *postdiv1 = 1;
 100                *postdiv2 = 1;
 101        }
 102        return 0;
 103}
 104
 105static struct rockchip_pll_rate_table *
 106rockchip_pll_clk_set_by_auto(ulong fin_hz,
 107                             ulong fout_hz)
 108{
 109        struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
 110        /* FIXME set postdiv1/2 always 1*/
 111        u32 foutvco = fout_hz;
 112        ulong fin_64, frac_64;
 113        u32 f_frac, postdiv1, postdiv2;
 114        ulong clk_gcd = 0;
 115
 116        if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
 117                return NULL;
 118
 119        rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco);
 120        rate_table->postdiv1 = postdiv1;
 121        rate_table->postdiv2 = postdiv2;
 122        rate_table->dsmpd = 1;
 123
 124        if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
 125                fin_hz /= MHZ;
 126                foutvco /= MHZ;
 127                clk_gcd = gcd(fin_hz, foutvco);
 128                rate_table->refdiv = fin_hz / clk_gcd;
 129                rate_table->fbdiv = foutvco / clk_gcd;
 130
 131                rate_table->frac = 0;
 132
 133                debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n",
 134                      fin_hz, fout_hz, clk_gcd);
 135                debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n",
 136                      rate_table->refdiv,
 137                      rate_table->fbdiv, rate_table->postdiv1,
 138                      rate_table->postdiv2);
 139        } else {
 140                debug("frac div,fin_hz = %ld,fout_hz = %ld\n",
 141                      fin_hz, fout_hz);
 142                debug("frac get postdiv1 = %d,  postdiv2 = %d, foutvco = %d\n",
 143                      rate_table->postdiv1, rate_table->postdiv2, foutvco);
 144                clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
 145                rate_table->refdiv = fin_hz / MHZ / clk_gcd;
 146                rate_table->fbdiv = foutvco / MHZ / clk_gcd;
 147                debug("frac get refdiv = %d,  fbdiv = %d\n",
 148                      rate_table->refdiv, rate_table->fbdiv);
 149
 150                rate_table->frac = 0;
 151
 152                f_frac = (foutvco % MHZ);
 153                fin_64 = fin_hz;
 154                fin_64 = fin_64 / rate_table->refdiv;
 155                frac_64 = f_frac << 24;
 156                frac_64 = frac_64 / fin_64;
 157                rate_table->frac = frac_64;
 158                if (rate_table->frac > 0)
 159                        rate_table->dsmpd = 0;
 160                debug("frac = %x\n", rate_table->frac);
 161        }
 162        return rate_table;
 163}
 164
 165static const struct rockchip_pll_rate_table *
 166rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate)
 167{
 168        struct rockchip_pll_rate_table  *rate_table = pll->rate_table;
 169
 170        while (rate_table->rate) {
 171                if (rate_table->rate == rate)
 172                        break;
 173                rate_table++;
 174        }
 175        if (rate_table->rate != rate)
 176                return rockchip_pll_clk_set_by_auto(24 * MHZ, rate);
 177        else
 178                return rate_table;
 179}
 180
 181static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
 182                               void __iomem *base, ulong pll_id,
 183                               ulong drate)
 184{
 185        const struct rockchip_pll_rate_table *rate;
 186
 187        rate = rockchip_get_pll_settings(pll, drate);
 188        if (!rate) {
 189                printf("%s unsupport rate\n", __func__);
 190                return -EINVAL;
 191        }
 192
 193        debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d\n",
 194              __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv);
 195        debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: %d\n",
 196              __func__, rate->rate, rate->postdiv2, rate->dsmpd, rate->frac);
 197
 198        /*
 199         * When power on or changing PLL setting,
 200         * we must force PLL into slow mode to ensure output stable clock.
 201         */
 202        rk_clrsetreg(base + pll->mode_offset,
 203                     pll->mode_mask << pll->mode_shift,
 204                     RKCLK_PLL_MODE_SLOW << pll->mode_shift);
 205
 206        /* Power down */
 207        rk_setreg(base + pll->con_offset + 0x4,
 208                  1 << RK3036_PLLCON1_PWRDOWN_SHIT);
 209
 210        rk_clrsetreg(base + pll->con_offset,
 211                     (RK3036_PLLCON0_POSTDIV1_MASK |
 212                     RK3036_PLLCON0_FBDIV_MASK),
 213                     (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) |
 214                     rate->fbdiv);
 215        rk_clrsetreg(base + pll->con_offset + 0x4,
 216                     (RK3036_PLLCON1_POSTDIV2_MASK |
 217                     RK3036_PLLCON1_REFDIV_MASK),
 218                     (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT |
 219                     rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT));
 220        if (!rate->dsmpd) {
 221                rk_clrsetreg(base + pll->con_offset + 0x4,
 222                             RK3036_PLLCON1_DSMPD_MASK,
 223                             rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT);
 224                writel((readl(base + pll->con_offset + 0x8) &
 225                        (~RK3036_PLLCON2_FRAC_MASK)) |
 226                            (rate->frac << RK3036_PLLCON2_FRAC_SHIFT),
 227                            base + pll->con_offset + 0x8);
 228        }
 229
 230        /* Power Up */
 231        rk_clrreg(base + pll->con_offset + 0x4,
 232                  1 << RK3036_PLLCON1_PWRDOWN_SHIT);
 233
 234        /* waiting for pll lock */
 235        while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift)))
 236                udelay(1);
 237
 238        rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
 239                     RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
 240        debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
 241              pll, readl(base + pll->con_offset),
 242              readl(base + pll->con_offset + 0x4),
 243              readl(base + pll->con_offset + 0x8),
 244              readl(base + pll->mode_offset));
 245
 246        return 0;
 247}
 248
 249static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll,
 250                                 void __iomem *base, ulong pll_id)
 251{
 252        u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac;
 253        u32 con = 0, shift, mask;
 254        ulong rate;
 255
 256        con = readl(base + pll->mode_offset);
 257        shift = pll->mode_shift;
 258        mask = pll->mode_mask << shift;
 259
 260        switch ((con & mask) >> shift) {
 261        case RKCLK_PLL_MODE_SLOW:
 262                return OSC_HZ;
 263        case RKCLK_PLL_MODE_NORMAL:
 264                /* normal mode */
 265                con = readl(base + pll->con_offset);
 266                postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >>
 267                           RK3036_PLLCON0_POSTDIV1_SHIFT;
 268                fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >>
 269                        RK3036_PLLCON0_FBDIV_SHIFT;
 270                con = readl(base + pll->con_offset + 0x4);
 271                postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >>
 272                           RK3036_PLLCON1_POSTDIV2_SHIFT;
 273                refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >>
 274                         RK3036_PLLCON1_REFDIV_SHIFT;
 275                dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >>
 276                        RK3036_PLLCON1_DSMPD_SHIFT;
 277                con = readl(base + pll->con_offset + 0x8);
 278                frac = (con & RK3036_PLLCON2_FRAC_MASK) >>
 279                        RK3036_PLLCON2_FRAC_SHIFT;
 280                rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
 281                if (dsmpd == 0) {
 282                        u64 frac_rate = OSC_HZ * (u64)frac;
 283
 284                        do_div(frac_rate, refdiv);
 285                        frac_rate >>= 24;
 286                        do_div(frac_rate, postdiv1);
 287                        do_div(frac_rate, postdiv1);
 288                        rate += frac_rate;
 289                }
 290                return rate;
 291        case RKCLK_PLL_MODE_DEEP:
 292        default:
 293                return 32768;
 294        }
 295}
 296
 297ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll,
 298                            void __iomem *base,
 299                            ulong pll_id)
 300{
 301        ulong rate = 0;
 302
 303        switch (pll->type) {
 304        case pll_rk3036:
 305                pll->mode_mask = PLL_MODE_MASK;
 306                rate = rk3036_pll_get_rate(pll, base, pll_id);
 307                break;
 308        case pll_rk3328:
 309                pll->mode_mask = PLL_RK3328_MODE_MASK;
 310                rate = rk3036_pll_get_rate(pll, base, pll_id);
 311                break;
 312        default:
 313                printf("%s: Unknown pll type for pll clk %ld\n",
 314                       __func__, pll_id);
 315        }
 316        return rate;
 317}
 318
 319int rockchip_pll_set_rate(struct rockchip_pll_clock *pll,
 320                          void __iomem *base, ulong pll_id,
 321                          ulong drate)
 322{
 323        int ret = 0;
 324
 325        if (rockchip_pll_get_rate(pll, base, pll_id) == drate)
 326                return 0;
 327
 328        switch (pll->type) {
 329        case pll_rk3036:
 330                pll->mode_mask = PLL_MODE_MASK;
 331                ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
 332                break;
 333        case pll_rk3328:
 334                pll->mode_mask = PLL_RK3328_MODE_MASK;
 335                ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
 336                break;
 337        default:
 338                printf("%s: Unknown pll type for pll clk %ld\n",
 339                       __func__, pll_id);
 340        }
 341        return ret;
 342}
 343
 344const struct rockchip_cpu_rate_table *
 345rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table,
 346                          ulong rate)
 347{
 348        struct rockchip_cpu_rate_table *ps = cpu_table;
 349
 350        while (ps->rate) {
 351                if (ps->rate == rate)
 352                        break;
 353                ps++;
 354        }
 355        if (ps->rate != rate)
 356                return NULL;
 357        else
 358                return ps;
 359}
 360
 361