12f0a72b1SElaine Zhang /* 22f0a72b1SElaine Zhang * (C) Copyright 2018 Rockchip Electronics Co., Ltd 32f0a72b1SElaine Zhang * 42f0a72b1SElaine Zhang * SPDX-License-Identifier: GPL-2.0 52f0a72b1SElaine Zhang */ 62f0a72b1SElaine Zhang #include <common.h> 72f0a72b1SElaine Zhang #include <bitfield.h> 82f0a72b1SElaine Zhang #include <clk-uclass.h> 92f0a72b1SElaine Zhang #include <dm.h> 102f0a72b1SElaine Zhang #include <errno.h> 112f0a72b1SElaine Zhang #include <asm/io.h> 122f0a72b1SElaine Zhang #include <asm/arch/clock.h> 132f0a72b1SElaine Zhang #include <asm/arch/hardware.h> 142f0a72b1SElaine Zhang #include <div64.h> 152f0a72b1SElaine Zhang 162f0a72b1SElaine Zhang static struct rockchip_pll_rate_table rockchip_auto_table; 172f0a72b1SElaine Zhang 182f0a72b1SElaine Zhang #define PLL_MODE_MASK 0x3 192f0a72b1SElaine Zhang #define PLL_RK3328_MODE_MASK 0x1 202f0a72b1SElaine Zhang 212f0a72b1SElaine Zhang #define RK3036_PLLCON0_FBDIV_MASK 0xfff 222f0a72b1SElaine Zhang #define RK3036_PLLCON0_FBDIV_SHIFT 0 232f0a72b1SElaine Zhang #define RK3036_PLLCON0_POSTDIV1_MASK 0x7 << 12 242f0a72b1SElaine Zhang #define RK3036_PLLCON0_POSTDIV1_SHIFT 12 252f0a72b1SElaine Zhang #define RK3036_PLLCON1_REFDIV_MASK 0x3f 262f0a72b1SElaine Zhang #define RK3036_PLLCON1_REFDIV_SHIFT 0 272f0a72b1SElaine Zhang #define RK3036_PLLCON1_POSTDIV2_MASK 0x7 << 6 282f0a72b1SElaine Zhang #define RK3036_PLLCON1_POSTDIV2_SHIFT 6 292f0a72b1SElaine Zhang #define RK3036_PLLCON1_DSMPD_MASK 0x1 << 12 302f0a72b1SElaine Zhang #define RK3036_PLLCON1_DSMPD_SHIFT 12 312f0a72b1SElaine Zhang #define RK3036_PLLCON2_FRAC_MASK 0xffffff 322f0a72b1SElaine Zhang #define RK3036_PLLCON2_FRAC_SHIFT 0 332f0a72b1SElaine Zhang #define RK3036_PLLCON1_PWRDOWN_SHIT 13 342f0a72b1SElaine Zhang 352f0a72b1SElaine Zhang #define MHZ 1000000 362f0a72b1SElaine Zhang #define KHZ 1000 372f0a72b1SElaine Zhang enum { 382f0a72b1SElaine Zhang OSC_HZ = 24 * 1000000, 392f0a72b1SElaine Zhang VCO_MAX_HZ = 3200U * 1000000, 402f0a72b1SElaine Zhang VCO_MIN_HZ = 800 * 1000000, 412f0a72b1SElaine Zhang OUTPUT_MAX_HZ = 3200U * 1000000, 422f0a72b1SElaine Zhang OUTPUT_MIN_HZ = 24 * 1000000, 43*a962a5fdSElaine Zhang RK3588_VCO_MIN_HZ = 2250U * 1000000, 44*a962a5fdSElaine Zhang RK3588_VCO_MAX_HZ = 4500U * 1000000, 45*a962a5fdSElaine Zhang RK3588_FOUT_MIN_HZ = 37U * 1000000, 46*a962a5fdSElaine Zhang RK3588_FOUT_MAX_HZ = 4500U * 1000000, 472f0a72b1SElaine Zhang }; 482f0a72b1SElaine Zhang 492f0a72b1SElaine Zhang #define MIN_FOUTVCO_FREQ (800 * MHZ) 502f0a72b1SElaine Zhang #define MAX_FOUTVCO_FREQ (2000 * MHZ) 512f0a72b1SElaine Zhang 522f0a72b1SElaine Zhang int gcd(int m, int n) 532f0a72b1SElaine Zhang { 542f0a72b1SElaine Zhang int t; 552f0a72b1SElaine Zhang 562f0a72b1SElaine Zhang while (m > 0) { 572f0a72b1SElaine Zhang if (n > m) { 582f0a72b1SElaine Zhang t = m; 592f0a72b1SElaine Zhang m = n; 602f0a72b1SElaine Zhang n = t; 612f0a72b1SElaine Zhang } /* swap */ 622f0a72b1SElaine Zhang m -= n; 632f0a72b1SElaine Zhang } 642f0a72b1SElaine Zhang return n; 652f0a72b1SElaine Zhang } 662f0a72b1SElaine Zhang 672f0a72b1SElaine Zhang /* 682f0a72b1SElaine Zhang * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): 692f0a72b1SElaine Zhang * Formulas also embedded within the Fractional PLL Verilog model: 702f0a72b1SElaine Zhang * If DSMPD = 1 (DSM is disabled, "integer mode") 712f0a72b1SElaine Zhang * FOUTVCO = FREF / REFDIV * FBDIV 722f0a72b1SElaine Zhang * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 732f0a72b1SElaine Zhang * Where: 742f0a72b1SElaine Zhang * FOUTVCO = Fractional PLL non-divided output frequency 752f0a72b1SElaine Zhang * FOUTPOSTDIV = Fractional PLL divided output frequency 762f0a72b1SElaine Zhang * (output of second post divider) 772f0a72b1SElaine Zhang * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) 782f0a72b1SElaine Zhang * REFDIV = Fractional PLL input reference clock divider 792f0a72b1SElaine Zhang * FBDIV = Integer value programmed into feedback divide 802f0a72b1SElaine Zhang * 812f0a72b1SElaine Zhang */ 822f0a72b1SElaine Zhang 832f0a72b1SElaine Zhang static int rockchip_pll_clk_set_postdiv(ulong fout_hz, 842f0a72b1SElaine Zhang u32 *postdiv1, 852f0a72b1SElaine Zhang u32 *postdiv2, 862f0a72b1SElaine Zhang u32 *foutvco) 872f0a72b1SElaine Zhang { 882f0a72b1SElaine Zhang ulong freq; 892f0a72b1SElaine Zhang 902f0a72b1SElaine Zhang if (fout_hz < MIN_FOUTVCO_FREQ) { 912f0a72b1SElaine Zhang for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) { 922f0a72b1SElaine Zhang for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) { 932f0a72b1SElaine Zhang freq = fout_hz * (*postdiv1) * (*postdiv2); 942f0a72b1SElaine Zhang if (freq >= MIN_FOUTVCO_FREQ && 952f0a72b1SElaine Zhang freq <= MAX_FOUTVCO_FREQ) { 962f0a72b1SElaine Zhang *foutvco = freq; 972f0a72b1SElaine Zhang return 0; 982f0a72b1SElaine Zhang } 992f0a72b1SElaine Zhang } 1002f0a72b1SElaine Zhang } 1012f0a72b1SElaine Zhang printf("Can't FIND postdiv1/2 to make fout=%lu in 800~2000M.\n", 1022f0a72b1SElaine Zhang fout_hz); 1032f0a72b1SElaine Zhang } else { 1042f0a72b1SElaine Zhang *postdiv1 = 1; 1052f0a72b1SElaine Zhang *postdiv2 = 1; 1062f0a72b1SElaine Zhang } 1072f0a72b1SElaine Zhang return 0; 1082f0a72b1SElaine Zhang } 1092f0a72b1SElaine Zhang 1102f0a72b1SElaine Zhang static struct rockchip_pll_rate_table * 1112f0a72b1SElaine Zhang rockchip_pll_clk_set_by_auto(ulong fin_hz, 1122f0a72b1SElaine Zhang ulong fout_hz) 1132f0a72b1SElaine Zhang { 1142f0a72b1SElaine Zhang struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; 1152f0a72b1SElaine Zhang /* FIXME set postdiv1/2 always 1*/ 1162f0a72b1SElaine Zhang u32 foutvco = fout_hz; 1172f0a72b1SElaine Zhang ulong fin_64, frac_64; 1182f0a72b1SElaine Zhang u32 f_frac, postdiv1, postdiv2; 1192f0a72b1SElaine Zhang ulong clk_gcd = 0; 1202f0a72b1SElaine Zhang 1212f0a72b1SElaine Zhang if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) 1222f0a72b1SElaine Zhang return NULL; 1232f0a72b1SElaine Zhang 1242f0a72b1SElaine Zhang rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco); 1252f0a72b1SElaine Zhang rate_table->postdiv1 = postdiv1; 1262f0a72b1SElaine Zhang rate_table->postdiv2 = postdiv2; 1272f0a72b1SElaine Zhang rate_table->dsmpd = 1; 1282f0a72b1SElaine Zhang 1292f0a72b1SElaine Zhang if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { 1302f0a72b1SElaine Zhang fin_hz /= MHZ; 1312f0a72b1SElaine Zhang foutvco /= MHZ; 1322f0a72b1SElaine Zhang clk_gcd = gcd(fin_hz, foutvco); 1332f0a72b1SElaine Zhang rate_table->refdiv = fin_hz / clk_gcd; 1342f0a72b1SElaine Zhang rate_table->fbdiv = foutvco / clk_gcd; 1352f0a72b1SElaine Zhang 1362f0a72b1SElaine Zhang rate_table->frac = 0; 1372f0a72b1SElaine Zhang 1382f0a72b1SElaine Zhang debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n", 1392f0a72b1SElaine Zhang fin_hz, fout_hz, clk_gcd); 1402f0a72b1SElaine Zhang debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n", 1412f0a72b1SElaine Zhang rate_table->refdiv, 1422f0a72b1SElaine Zhang rate_table->fbdiv, rate_table->postdiv1, 1432f0a72b1SElaine Zhang rate_table->postdiv2); 1442f0a72b1SElaine Zhang } else { 1452f0a72b1SElaine Zhang debug("frac div,fin_hz = %ld,fout_hz = %ld\n", 1462f0a72b1SElaine Zhang fin_hz, fout_hz); 1472f0a72b1SElaine Zhang debug("frac get postdiv1 = %d, postdiv2 = %d, foutvco = %d\n", 1482f0a72b1SElaine Zhang rate_table->postdiv1, rate_table->postdiv2, foutvco); 1492f0a72b1SElaine Zhang clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ); 1502f0a72b1SElaine Zhang rate_table->refdiv = fin_hz / MHZ / clk_gcd; 1512f0a72b1SElaine Zhang rate_table->fbdiv = foutvco / MHZ / clk_gcd; 1522f0a72b1SElaine Zhang debug("frac get refdiv = %d, fbdiv = %d\n", 1532f0a72b1SElaine Zhang rate_table->refdiv, rate_table->fbdiv); 1542f0a72b1SElaine Zhang 1552f0a72b1SElaine Zhang rate_table->frac = 0; 1562f0a72b1SElaine Zhang 1572f0a72b1SElaine Zhang f_frac = (foutvco % MHZ); 1582f0a72b1SElaine Zhang fin_64 = fin_hz; 1592f0a72b1SElaine Zhang fin_64 = fin_64 / rate_table->refdiv; 1602f0a72b1SElaine Zhang frac_64 = f_frac << 24; 1612f0a72b1SElaine Zhang frac_64 = frac_64 / fin_64; 1622f0a72b1SElaine Zhang rate_table->frac = frac_64; 1632f0a72b1SElaine Zhang if (rate_table->frac > 0) 1642f0a72b1SElaine Zhang rate_table->dsmpd = 0; 1652f0a72b1SElaine Zhang debug("frac = %x\n", rate_table->frac); 1662f0a72b1SElaine Zhang } 1672f0a72b1SElaine Zhang return rate_table; 1682f0a72b1SElaine Zhang } 1692f0a72b1SElaine Zhang 170*a962a5fdSElaine Zhang static struct rockchip_pll_rate_table * 171*a962a5fdSElaine Zhang rk3588_pll_clk_set_by_auto(unsigned long fin_hz, 172*a962a5fdSElaine Zhang unsigned long fout_hz) 173*a962a5fdSElaine Zhang { 174*a962a5fdSElaine Zhang struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; 175*a962a5fdSElaine Zhang u32 p, m, s; 176*a962a5fdSElaine Zhang ulong fvco, fref, fout, ffrac; 177*a962a5fdSElaine Zhang 178*a962a5fdSElaine Zhang if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) 179*a962a5fdSElaine Zhang return NULL; 180*a962a5fdSElaine Zhang 181*a962a5fdSElaine Zhang if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ) 182*a962a5fdSElaine Zhang return NULL; 183*a962a5fdSElaine Zhang 184*a962a5fdSElaine Zhang if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { 185*a962a5fdSElaine Zhang for (s = 0; s <= 6; s++) { 186*a962a5fdSElaine Zhang fvco = fout_hz << s; 187*a962a5fdSElaine Zhang if (fvco < RK3588_VCO_MIN_HZ || 188*a962a5fdSElaine Zhang fvco > RK3588_VCO_MAX_HZ) 189*a962a5fdSElaine Zhang continue; 190*a962a5fdSElaine Zhang for (p = 2; p <= 4; p++) { 191*a962a5fdSElaine Zhang for (m = 64; m <= 1023; m++) { 192*a962a5fdSElaine Zhang if (fvco == m * fin_hz / p) { 193*a962a5fdSElaine Zhang rate_table->p = p; 194*a962a5fdSElaine Zhang rate_table->m = m; 195*a962a5fdSElaine Zhang rate_table->s = s; 196*a962a5fdSElaine Zhang rate_table->k = 0; 197*a962a5fdSElaine Zhang return rate_table; 198*a962a5fdSElaine Zhang } 199*a962a5fdSElaine Zhang } 200*a962a5fdSElaine Zhang } 201*a962a5fdSElaine Zhang } 202*a962a5fdSElaine Zhang pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); 203*a962a5fdSElaine Zhang } else { 204*a962a5fdSElaine Zhang fout = (fout_hz / MHZ) * MHZ; 205*a962a5fdSElaine Zhang ffrac = (fout_hz % MHZ); 206*a962a5fdSElaine Zhang for (s = 0; s <= 6; s++) { 207*a962a5fdSElaine Zhang fvco = fout << s; 208*a962a5fdSElaine Zhang if (fvco < RK3588_VCO_MIN_HZ || 209*a962a5fdSElaine Zhang fvco > RK3588_VCO_MAX_HZ) 210*a962a5fdSElaine Zhang continue; 211*a962a5fdSElaine Zhang for (p = 1; p <= 4; p++) { 212*a962a5fdSElaine Zhang for (m = 64; m <= 1023; m++) { 213*a962a5fdSElaine Zhang if (fvco == m * fin_hz / p) { 214*a962a5fdSElaine Zhang rate_table->p = p; 215*a962a5fdSElaine Zhang rate_table->m = m; 216*a962a5fdSElaine Zhang rate_table->s = s; 217*a962a5fdSElaine Zhang fref = fin_hz / p; 218*a962a5fdSElaine Zhang fout = (ffrac << s) * 65535; 219*a962a5fdSElaine Zhang rate_table->k = fout / fref; 220*a962a5fdSElaine Zhang return rate_table; 221*a962a5fdSElaine Zhang } 222*a962a5fdSElaine Zhang } 223*a962a5fdSElaine Zhang } 224*a962a5fdSElaine Zhang } 225*a962a5fdSElaine Zhang pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); 226*a962a5fdSElaine Zhang } 227*a962a5fdSElaine Zhang return NULL; 228*a962a5fdSElaine Zhang } 229*a962a5fdSElaine Zhang 2302f0a72b1SElaine Zhang static const struct rockchip_pll_rate_table * 2312f0a72b1SElaine Zhang rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) 2322f0a72b1SElaine Zhang { 2332f0a72b1SElaine Zhang struct rockchip_pll_rate_table *rate_table = pll->rate_table; 2342f0a72b1SElaine Zhang 2352f0a72b1SElaine Zhang while (rate_table->rate) { 2362f0a72b1SElaine Zhang if (rate_table->rate == rate) 2372f0a72b1SElaine Zhang break; 2382f0a72b1SElaine Zhang rate_table++; 2392f0a72b1SElaine Zhang } 240*a962a5fdSElaine Zhang if (rate_table->rate != rate) { 241*a962a5fdSElaine Zhang if (pll->type == pll_rk3588) 242*a962a5fdSElaine Zhang return rk3588_pll_clk_set_by_auto(24 * MHZ, rate); 2432f0a72b1SElaine Zhang else 244*a962a5fdSElaine Zhang return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); 245*a962a5fdSElaine Zhang } else { 2462f0a72b1SElaine Zhang return rate_table; 2472f0a72b1SElaine Zhang } 248*a962a5fdSElaine Zhang } 2492f0a72b1SElaine Zhang 2502f0a72b1SElaine Zhang static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, 2512f0a72b1SElaine Zhang void __iomem *base, ulong pll_id, 2522f0a72b1SElaine Zhang ulong drate) 2532f0a72b1SElaine Zhang { 2542f0a72b1SElaine Zhang const struct rockchip_pll_rate_table *rate; 2552f0a72b1SElaine Zhang 2562f0a72b1SElaine Zhang rate = rockchip_get_pll_settings(pll, drate); 2572f0a72b1SElaine Zhang if (!rate) { 2582f0a72b1SElaine Zhang printf("%s unsupport rate\n", __func__); 2592f0a72b1SElaine Zhang return -EINVAL; 2602f0a72b1SElaine Zhang } 2612f0a72b1SElaine Zhang 2622f0a72b1SElaine Zhang debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d\n", 2632f0a72b1SElaine Zhang __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv); 2642f0a72b1SElaine Zhang debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: %d\n", 2652f0a72b1SElaine Zhang __func__, rate->rate, rate->postdiv2, rate->dsmpd, rate->frac); 2662f0a72b1SElaine Zhang 2672f0a72b1SElaine Zhang /* 2682f0a72b1SElaine Zhang * When power on or changing PLL setting, 2692f0a72b1SElaine Zhang * we must force PLL into slow mode to ensure output stable clock. 2702f0a72b1SElaine Zhang */ 2712f0a72b1SElaine Zhang rk_clrsetreg(base + pll->mode_offset, 2722f0a72b1SElaine Zhang pll->mode_mask << pll->mode_shift, 2732f0a72b1SElaine Zhang RKCLK_PLL_MODE_SLOW << pll->mode_shift); 2742f0a72b1SElaine Zhang 2752f0a72b1SElaine Zhang /* Power down */ 2762f0a72b1SElaine Zhang rk_setreg(base + pll->con_offset + 0x4, 2772f0a72b1SElaine Zhang 1 << RK3036_PLLCON1_PWRDOWN_SHIT); 2782f0a72b1SElaine Zhang 2792f0a72b1SElaine Zhang rk_clrsetreg(base + pll->con_offset, 2802f0a72b1SElaine Zhang (RK3036_PLLCON0_POSTDIV1_MASK | 2812f0a72b1SElaine Zhang RK3036_PLLCON0_FBDIV_MASK), 2822f0a72b1SElaine Zhang (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) | 2832f0a72b1SElaine Zhang rate->fbdiv); 2842f0a72b1SElaine Zhang rk_clrsetreg(base + pll->con_offset + 0x4, 2852f0a72b1SElaine Zhang (RK3036_PLLCON1_POSTDIV2_MASK | 2862f0a72b1SElaine Zhang RK3036_PLLCON1_REFDIV_MASK), 2872f0a72b1SElaine Zhang (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT | 2882f0a72b1SElaine Zhang rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT)); 2892f0a72b1SElaine Zhang if (!rate->dsmpd) { 2902f0a72b1SElaine Zhang rk_clrsetreg(base + pll->con_offset + 0x4, 2912f0a72b1SElaine Zhang RK3036_PLLCON1_DSMPD_MASK, 2922f0a72b1SElaine Zhang rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT); 2932f0a72b1SElaine Zhang writel((readl(base + pll->con_offset + 0x8) & 2942f0a72b1SElaine Zhang (~RK3036_PLLCON2_FRAC_MASK)) | 2952f0a72b1SElaine Zhang (rate->frac << RK3036_PLLCON2_FRAC_SHIFT), 2962f0a72b1SElaine Zhang base + pll->con_offset + 0x8); 2972f0a72b1SElaine Zhang } 2982f0a72b1SElaine Zhang 2992f0a72b1SElaine Zhang /* Power Up */ 3002f0a72b1SElaine Zhang rk_clrreg(base + pll->con_offset + 0x4, 3012f0a72b1SElaine Zhang 1 << RK3036_PLLCON1_PWRDOWN_SHIT); 3022f0a72b1SElaine Zhang 3032f0a72b1SElaine Zhang /* waiting for pll lock */ 304f680c019SJoseph Chen while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift))) { 3052f0a72b1SElaine Zhang udelay(1); 306f680c019SJoseph Chen debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id); 307f680c019SJoseph Chen } 3082f0a72b1SElaine Zhang 3092f0a72b1SElaine Zhang rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, 3102f0a72b1SElaine Zhang RKCLK_PLL_MODE_NORMAL << pll->mode_shift); 3112f0a72b1SElaine Zhang debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", 3122f0a72b1SElaine Zhang pll, readl(base + pll->con_offset), 3132f0a72b1SElaine Zhang readl(base + pll->con_offset + 0x4), 3142f0a72b1SElaine Zhang readl(base + pll->con_offset + 0x8), 3152f0a72b1SElaine Zhang readl(base + pll->mode_offset)); 3162f0a72b1SElaine Zhang 3172f0a72b1SElaine Zhang return 0; 3182f0a72b1SElaine Zhang } 3192f0a72b1SElaine Zhang 3202f0a72b1SElaine Zhang static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, 3212f0a72b1SElaine Zhang void __iomem *base, ulong pll_id) 3222f0a72b1SElaine Zhang { 3232f0a72b1SElaine Zhang u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac; 3242f0a72b1SElaine Zhang u32 con = 0, shift, mask; 325a737bf22SFinley Xiao ulong rate; 3262f0a72b1SElaine Zhang 3272f0a72b1SElaine Zhang con = readl(base + pll->mode_offset); 3282f0a72b1SElaine Zhang shift = pll->mode_shift; 3292f0a72b1SElaine Zhang mask = pll->mode_mask << shift; 3302f0a72b1SElaine Zhang 3312f0a72b1SElaine Zhang switch ((con & mask) >> shift) { 3322f0a72b1SElaine Zhang case RKCLK_PLL_MODE_SLOW: 3332f0a72b1SElaine Zhang return OSC_HZ; 3342f0a72b1SElaine Zhang case RKCLK_PLL_MODE_NORMAL: 3352f0a72b1SElaine Zhang /* normal mode */ 3362f0a72b1SElaine Zhang con = readl(base + pll->con_offset); 3372f0a72b1SElaine Zhang postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >> 3382f0a72b1SElaine Zhang RK3036_PLLCON0_POSTDIV1_SHIFT; 3392f0a72b1SElaine Zhang fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >> 3402f0a72b1SElaine Zhang RK3036_PLLCON0_FBDIV_SHIFT; 3412f0a72b1SElaine Zhang con = readl(base + pll->con_offset + 0x4); 3422f0a72b1SElaine Zhang postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >> 3432f0a72b1SElaine Zhang RK3036_PLLCON1_POSTDIV2_SHIFT; 3442f0a72b1SElaine Zhang refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >> 3452f0a72b1SElaine Zhang RK3036_PLLCON1_REFDIV_SHIFT; 3462f0a72b1SElaine Zhang dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >> 3472f0a72b1SElaine Zhang RK3036_PLLCON1_DSMPD_SHIFT; 3482f0a72b1SElaine Zhang con = readl(base + pll->con_offset + 0x8); 3492f0a72b1SElaine Zhang frac = (con & RK3036_PLLCON2_FRAC_MASK) >> 3502f0a72b1SElaine Zhang RK3036_PLLCON2_FRAC_SHIFT; 351a737bf22SFinley Xiao rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; 3522f0a72b1SElaine Zhang if (dsmpd == 0) { 353a737bf22SFinley Xiao u64 frac_rate = OSC_HZ * (u64)frac; 3542f0a72b1SElaine Zhang 355a737bf22SFinley Xiao do_div(frac_rate, refdiv); 356a737bf22SFinley Xiao frac_rate >>= 24; 357a737bf22SFinley Xiao do_div(frac_rate, postdiv1); 358a737bf22SFinley Xiao do_div(frac_rate, postdiv1); 3592f0a72b1SElaine Zhang rate += frac_rate; 3602f0a72b1SElaine Zhang } 3612f0a72b1SElaine Zhang return rate; 3622f0a72b1SElaine Zhang case RKCLK_PLL_MODE_DEEP: 3632f0a72b1SElaine Zhang default: 3642f0a72b1SElaine Zhang return 32768; 3652f0a72b1SElaine Zhang } 3662f0a72b1SElaine Zhang } 3672f0a72b1SElaine Zhang 368*a962a5fdSElaine Zhang #define RK3588_PLLCON(i) ((i) * 0x4) 369*a962a5fdSElaine Zhang #define RK3588_PLLCON0_M_MASK 0x3ff << 0 370*a962a5fdSElaine Zhang #define RK3588_PLLCON0_M_SHIFT 0 371*a962a5fdSElaine Zhang #define RK3588_PLLCON1_P_MASK 0x3f << 0 372*a962a5fdSElaine Zhang #define RK3588_PLLCON1_P_SHIFT 0 373*a962a5fdSElaine Zhang #define RK3588_PLLCON1_S_MASK 0x7 << 6 374*a962a5fdSElaine Zhang #define RK3588_PLLCON1_S_SHIFT 6 375*a962a5fdSElaine Zhang #define RK3588_PLLCON2_K_MASK 0xffff 376*a962a5fdSElaine Zhang #define RK3588_PLLCON2_K_SHIFT 0 377*a962a5fdSElaine Zhang #define RK3588_PLLCON1_PWRDOWN BIT(13) 378*a962a5fdSElaine Zhang #define RK3588_PLLCON6_LOCK_STATUS BIT(15) 379*a962a5fdSElaine Zhang #define RK3588_B0PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x50000 + 0x300) 380*a962a5fdSElaine Zhang #define RK3588_B1PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x52000 + 0x300) 381*a962a5fdSElaine Zhang #define RK3588_LPLL_CLKSEL_CON(i) ((i) * 0x4 + 0x58000 + 0x300) 382*a962a5fdSElaine Zhang 383*a962a5fdSElaine Zhang static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll, 384*a962a5fdSElaine Zhang void __iomem *base, ulong pll_id, 385*a962a5fdSElaine Zhang ulong drate) 386*a962a5fdSElaine Zhang { 387*a962a5fdSElaine Zhang const struct rockchip_pll_rate_table *rate; 388*a962a5fdSElaine Zhang 389*a962a5fdSElaine Zhang rate = rockchip_get_pll_settings(pll, drate); 390*a962a5fdSElaine Zhang if (!rate) { 391*a962a5fdSElaine Zhang printf("%s unsupported rate\n", __func__); 392*a962a5fdSElaine Zhang return -EINVAL; 393*a962a5fdSElaine Zhang } 394*a962a5fdSElaine Zhang 395*a962a5fdSElaine Zhang debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n", 396*a962a5fdSElaine Zhang __func__, rate->rate, rate->p, rate->m, rate->s, rate->k); 397*a962a5fdSElaine Zhang 398*a962a5fdSElaine Zhang /* 399*a962a5fdSElaine Zhang * When power on or changing PLL setting, 400*a962a5fdSElaine Zhang * we must force PLL into slow mode to ensure output stable clock. 401*a962a5fdSElaine Zhang */ 402*a962a5fdSElaine Zhang rk_clrsetreg(base + pll->mode_offset, 403*a962a5fdSElaine Zhang pll->mode_mask << pll->mode_shift, 404*a962a5fdSElaine Zhang RKCLK_PLL_MODE_SLOW << pll->mode_shift); 405*a962a5fdSElaine Zhang if (pll_id == 0) 406*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), 407*a962a5fdSElaine Zhang pll->mode_mask << 6, 408*a962a5fdSElaine Zhang RKCLK_PLL_MODE_SLOW << 6); 409*a962a5fdSElaine Zhang else if (pll_id == 1) 410*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), 411*a962a5fdSElaine Zhang pll->mode_mask << 6, 412*a962a5fdSElaine Zhang RKCLK_PLL_MODE_SLOW << 6); 413*a962a5fdSElaine Zhang else if (pll_id == 2) 414*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), 415*a962a5fdSElaine Zhang pll->mode_mask << 14, 416*a962a5fdSElaine Zhang RKCLK_PLL_MODE_SLOW << 14); 417*a962a5fdSElaine Zhang 418*a962a5fdSElaine Zhang /* Power down */ 419*a962a5fdSElaine Zhang rk_setreg(base + pll->con_offset + RK3588_PLLCON(1), 420*a962a5fdSElaine Zhang RK3588_PLLCON1_PWRDOWN); 421*a962a5fdSElaine Zhang 422*a962a5fdSElaine Zhang rk_clrsetreg(base + pll->con_offset, 423*a962a5fdSElaine Zhang RK3588_PLLCON0_M_MASK, 424*a962a5fdSElaine Zhang (rate->m << RK3588_PLLCON0_M_SHIFT)); 425*a962a5fdSElaine Zhang rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1), 426*a962a5fdSElaine Zhang (RK3588_PLLCON1_P_MASK | 427*a962a5fdSElaine Zhang RK3588_PLLCON1_S_MASK), 428*a962a5fdSElaine Zhang (rate->p << RK3588_PLLCON1_P_SHIFT | 429*a962a5fdSElaine Zhang rate->s << RK3588_PLLCON1_S_SHIFT)); 430*a962a5fdSElaine Zhang if (!rate->k) { 431*a962a5fdSElaine Zhang rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2), 432*a962a5fdSElaine Zhang RK3588_PLLCON2_K_MASK, 433*a962a5fdSElaine Zhang rate->k << RK3588_PLLCON2_K_SHIFT); 434*a962a5fdSElaine Zhang } 435*a962a5fdSElaine Zhang /* Power up */ 436*a962a5fdSElaine Zhang rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1), 437*a962a5fdSElaine Zhang RK3588_PLLCON1_PWRDOWN); 438*a962a5fdSElaine Zhang 439*a962a5fdSElaine Zhang /* waiting for pll lock */ 440*a962a5fdSElaine Zhang while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) & 441*a962a5fdSElaine Zhang RK3588_PLLCON6_LOCK_STATUS)) { 442*a962a5fdSElaine Zhang udelay(1); 443*a962a5fdSElaine Zhang debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id); 444*a962a5fdSElaine Zhang } 445*a962a5fdSElaine Zhang 446*a962a5fdSElaine Zhang rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, 447*a962a5fdSElaine Zhang RKCLK_PLL_MODE_NORMAL << pll->mode_shift); 448*a962a5fdSElaine Zhang if (pll_id == 0) 449*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), 450*a962a5fdSElaine Zhang pll->mode_mask << 6, 451*a962a5fdSElaine Zhang 2 << 6); 452*a962a5fdSElaine Zhang else if (pll_id == 1) 453*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), 454*a962a5fdSElaine Zhang pll->mode_mask << 6, 455*a962a5fdSElaine Zhang 2 << 6); 456*a962a5fdSElaine Zhang else if (pll_id == 2) 457*a962a5fdSElaine Zhang rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), 458*a962a5fdSElaine Zhang pll->mode_mask << 14, 459*a962a5fdSElaine Zhang 2 << 14); 460*a962a5fdSElaine Zhang debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", 461*a962a5fdSElaine Zhang pll, readl(base + pll->con_offset), 462*a962a5fdSElaine Zhang readl(base + pll->con_offset + 0x4), 463*a962a5fdSElaine Zhang readl(base + pll->con_offset + 0x8), 464*a962a5fdSElaine Zhang readl(base + pll->mode_offset)); 465*a962a5fdSElaine Zhang 466*a962a5fdSElaine Zhang return 0; 467*a962a5fdSElaine Zhang } 468*a962a5fdSElaine Zhang 469*a962a5fdSElaine Zhang static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, 470*a962a5fdSElaine Zhang void __iomem *base, ulong pll_id) 471*a962a5fdSElaine Zhang { 472*a962a5fdSElaine Zhang u32 m, p, s, k; 473*a962a5fdSElaine Zhang u32 con = 0, shift, mode; 474*a962a5fdSElaine Zhang u64 rate, postdiv; 475*a962a5fdSElaine Zhang 476*a962a5fdSElaine Zhang con = readl(base + pll->mode_offset); 477*a962a5fdSElaine Zhang shift = pll->mode_shift; 478*a962a5fdSElaine Zhang if (pll_id == 8) 479*a962a5fdSElaine Zhang mode = RKCLK_PLL_MODE_NORMAL; 480*a962a5fdSElaine Zhang else 481*a962a5fdSElaine Zhang mode = (con & (pll->mode_mask << shift)) >> shift; 482*a962a5fdSElaine Zhang switch (mode) { 483*a962a5fdSElaine Zhang case RKCLK_PLL_MODE_SLOW: 484*a962a5fdSElaine Zhang return OSC_HZ; 485*a962a5fdSElaine Zhang case RKCLK_PLL_MODE_NORMAL: 486*a962a5fdSElaine Zhang /* normal mode */ 487*a962a5fdSElaine Zhang con = readl(base + pll->con_offset); 488*a962a5fdSElaine Zhang m = (con & RK3588_PLLCON0_M_MASK) >> 489*a962a5fdSElaine Zhang RK3588_PLLCON0_M_SHIFT; 490*a962a5fdSElaine Zhang con = readl(base + pll->con_offset + RK3588_PLLCON(1)); 491*a962a5fdSElaine Zhang p = (con & RK3588_PLLCON1_P_MASK) >> 492*a962a5fdSElaine Zhang RK3036_PLLCON0_FBDIV_SHIFT; 493*a962a5fdSElaine Zhang s = (con & RK3588_PLLCON1_S_MASK) >> 494*a962a5fdSElaine Zhang RK3588_PLLCON1_S_SHIFT; 495*a962a5fdSElaine Zhang con = readl(base + pll->con_offset + RK3588_PLLCON(2)); 496*a962a5fdSElaine Zhang k = (con & RK3588_PLLCON2_K_MASK) >> 497*a962a5fdSElaine Zhang RK3588_PLLCON2_K_SHIFT; 498*a962a5fdSElaine Zhang 499*a962a5fdSElaine Zhang rate = OSC_HZ / p; 500*a962a5fdSElaine Zhang rate *= m; 501*a962a5fdSElaine Zhang if (k) { 502*a962a5fdSElaine Zhang /* fractional mode */ 503*a962a5fdSElaine Zhang u64 frac_rate64 = OSC_HZ * k; 504*a962a5fdSElaine Zhang 505*a962a5fdSElaine Zhang postdiv = p * 65535; 506*a962a5fdSElaine Zhang do_div(frac_rate64, postdiv); 507*a962a5fdSElaine Zhang rate += frac_rate64; 508*a962a5fdSElaine Zhang } 509*a962a5fdSElaine Zhang rate = rate >> s; 510*a962a5fdSElaine Zhang return rate; 511*a962a5fdSElaine Zhang case RKCLK_PLL_MODE_DEEP: 512*a962a5fdSElaine Zhang default: 513*a962a5fdSElaine Zhang return 32768; 514*a962a5fdSElaine Zhang } 515*a962a5fdSElaine Zhang } 516*a962a5fdSElaine Zhang 5172f0a72b1SElaine Zhang ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, 5182f0a72b1SElaine Zhang void __iomem *base, 5192f0a72b1SElaine Zhang ulong pll_id) 5202f0a72b1SElaine Zhang { 5212f0a72b1SElaine Zhang ulong rate = 0; 5222f0a72b1SElaine Zhang 5232f0a72b1SElaine Zhang switch (pll->type) { 5242f0a72b1SElaine Zhang case pll_rk3036: 5252f0a72b1SElaine Zhang pll->mode_mask = PLL_MODE_MASK; 5262f0a72b1SElaine Zhang rate = rk3036_pll_get_rate(pll, base, pll_id); 5272f0a72b1SElaine Zhang break; 5282f0a72b1SElaine Zhang case pll_rk3328: 5292f0a72b1SElaine Zhang pll->mode_mask = PLL_RK3328_MODE_MASK; 5302f0a72b1SElaine Zhang rate = rk3036_pll_get_rate(pll, base, pll_id); 5312f0a72b1SElaine Zhang break; 532*a962a5fdSElaine Zhang case pll_rk3588: 533*a962a5fdSElaine Zhang pll->mode_mask = PLL_MODE_MASK; 534*a962a5fdSElaine Zhang rate = rk3588_pll_get_rate(pll, base, pll_id); 535*a962a5fdSElaine Zhang break; 5362f0a72b1SElaine Zhang default: 5372f0a72b1SElaine Zhang printf("%s: Unknown pll type for pll clk %ld\n", 5382f0a72b1SElaine Zhang __func__, pll_id); 5392f0a72b1SElaine Zhang } 5402f0a72b1SElaine Zhang return rate; 5412f0a72b1SElaine Zhang } 5422f0a72b1SElaine Zhang 5432f0a72b1SElaine Zhang int rockchip_pll_set_rate(struct rockchip_pll_clock *pll, 5442f0a72b1SElaine Zhang void __iomem *base, ulong pll_id, 5452f0a72b1SElaine Zhang ulong drate) 5462f0a72b1SElaine Zhang { 5472f0a72b1SElaine Zhang int ret = 0; 5482f0a72b1SElaine Zhang 5492f0a72b1SElaine Zhang if (rockchip_pll_get_rate(pll, base, pll_id) == drate) 5502f0a72b1SElaine Zhang return 0; 5512f0a72b1SElaine Zhang 5522f0a72b1SElaine Zhang switch (pll->type) { 5532f0a72b1SElaine Zhang case pll_rk3036: 5542f0a72b1SElaine Zhang pll->mode_mask = PLL_MODE_MASK; 5552f0a72b1SElaine Zhang ret = rk3036_pll_set_rate(pll, base, pll_id, drate); 5562f0a72b1SElaine Zhang break; 5572f0a72b1SElaine Zhang case pll_rk3328: 5582f0a72b1SElaine Zhang pll->mode_mask = PLL_RK3328_MODE_MASK; 5592f0a72b1SElaine Zhang ret = rk3036_pll_set_rate(pll, base, pll_id, drate); 5602f0a72b1SElaine Zhang break; 561*a962a5fdSElaine Zhang case pll_rk3588: 562*a962a5fdSElaine Zhang pll->mode_mask = PLL_MODE_MASK; 563*a962a5fdSElaine Zhang ret = rk3588_pll_set_rate(pll, base, pll_id, drate); 564*a962a5fdSElaine Zhang break; 5652f0a72b1SElaine Zhang default: 5662f0a72b1SElaine Zhang printf("%s: Unknown pll type for pll clk %ld\n", 5672f0a72b1SElaine Zhang __func__, pll_id); 5682f0a72b1SElaine Zhang } 5692f0a72b1SElaine Zhang return ret; 5702f0a72b1SElaine Zhang } 5712f0a72b1SElaine Zhang 5722f0a72b1SElaine Zhang const struct rockchip_cpu_rate_table * 5732f0a72b1SElaine Zhang rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table, 5742f0a72b1SElaine Zhang ulong rate) 5752f0a72b1SElaine Zhang { 5762f0a72b1SElaine Zhang struct rockchip_cpu_rate_table *ps = cpu_table; 5772f0a72b1SElaine Zhang 5782f0a72b1SElaine Zhang while (ps->rate) { 5792f0a72b1SElaine Zhang if (ps->rate == rate) 5802f0a72b1SElaine Zhang break; 5812f0a72b1SElaine Zhang ps++; 5822f0a72b1SElaine Zhang } 5832f0a72b1SElaine Zhang if (ps->rate != rate) 5842f0a72b1SElaine Zhang return NULL; 5852f0a72b1SElaine Zhang else 5862f0a72b1SElaine Zhang return ps; 5872f0a72b1SElaine Zhang } 5882f0a72b1SElaine Zhang 589