[U-Boot] [PATCH v1] rockchip: clk: pll: add common pll setting funcs【请注意,邮件由u-boot-bounces at lists.denx.de代发】 funcs

Kever Yang kever.yang at rock-chips.com
Mon Nov 18 02:57:06 UTC 2019


On 2019/11/10 下午10:31, Kever Yang wrote:
>
> On 2019/10/25 上午9:42, Elaine Zhang wrote:
>> Common PLL setup function, compatible with different SOC.
>> Mainly for the subsequent new SOC use.
>>
>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>
> Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
>
Applied to u-boot-rockchip master .

Thanks,
- Kever
> Thanks,
> - Kever
>> ---
>>   arch/arm/include/asm/arch-rockchip/clock.h |  76 +++++
>>   drivers/clk/rockchip/Makefile              |   1 +
>>   drivers/clk/rockchip/clk_pll.c             | 361 +++++++++++++++++++++
>>   3 files changed, 438 insertions(+)
>>   create mode 100644 drivers/clk/rockchip/clk_pll.c
>>
>> diff --git a/arch/arm/include/asm/arch-rockchip/clock.h 
>> b/arch/arm/include/asm/arch-rockchip/clock.h
>> index 0eb19ca86f29..a79c904a3e14 100644
>> --- a/arch/arm/include/asm/arch-rockchip/clock.h
>> +++ b/arch/arm/include/asm/arch-rockchip/clock.h
>> @@ -9,6 +9,7 @@
>>   /* define pll mode */
>>   #define RKCLK_PLL_MODE_SLOW        0
>>   #define RKCLK_PLL_MODE_NORMAL        1
>> +#define RKCLK_PLL_MODE_DEEP        2
>>     enum {
>>       ROCKCHIP_SYSCON_NOC,
>> @@ -33,6 +34,81 @@ enum rk_clk_id {
>>       CLK_COUNT,
>>   };
>>   +#define PLL(_type, _id, _con, _mode, _mshift,            \
>> +         _lshift, _pflags, _rtable)            \
>> +    {                            \
>> +        .id        = _id,                \
>> +        .type        = _type,            \
>> +        .con_offset    = _con,                \
>> +        .mode_offset    = _mode,            \
>> +        .mode_shift    = _mshift,            \
>> +        .lock_shift    = _lshift,            \
>> +        .pll_flags    = _pflags,            \
>> +        .rate_table    = _rtable,            \
>> +    }
>> +
>> +#define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,    \
>> +            _postdiv2, _dsmpd, _frac)        \
>> +{                                \
>> +    .rate    = _rate##U,                    \
>> +    .fbdiv = _fbdiv,                    \
>> +    .postdiv1 = _postdiv1,                    \
>> +    .refdiv = _refdiv,                    \
>> +    .postdiv2 = _postdiv2,                    \
>> +    .dsmpd = _dsmpd,                    \
>> +    .frac = _frac,                        \
>> +}
>> +
>> +struct rockchip_pll_rate_table {
>> +    unsigned long rate;
>> +    unsigned int nr;
>> +    unsigned int nf;
>> +    unsigned int no;
>> +    unsigned int nb;
>> +    /* for RK3036/RK3399 */
>> +    unsigned int fbdiv;
>> +    unsigned int postdiv1;
>> +    unsigned int refdiv;
>> +    unsigned int postdiv2;
>> +    unsigned int dsmpd;
>> +    unsigned int frac;
>> +};
>> +
>> +enum rockchip_pll_type {
>> +    pll_rk3036,
>> +    pll_rk3066,
>> +    pll_rk3328,
>> +    pll_rk3366,
>> +    pll_rk3399,
>> +};
>> +
>> +struct rockchip_pll_clock {
>> +    unsigned int            id;
>> +    unsigned int            con_offset;
>> +    unsigned int            mode_offset;
>> +    unsigned int            mode_shift;
>> +    unsigned int            lock_shift;
>> +    enum rockchip_pll_type        type;
>> +    unsigned int            pll_flags;
>> +    struct rockchip_pll_rate_table *rate_table;
>> +    unsigned int            mode_mask;
>> +};
>> +
>> +struct rockchip_cpu_rate_table {
>> +    unsigned long rate;
>> +    unsigned int aclk_div;
>> +    unsigned int pclk_div;
>> +};
>> +
>> +int rockchip_pll_set_rate(struct rockchip_pll_clock *pll,
>> +              void __iomem *base, ulong clk_id,
>> +              ulong drate);
>> +ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll,
>> +                void __iomem *base, ulong clk_id);
>> +const struct rockchip_cpu_rate_table *
>> +rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table,
>> +              ulong rate);
>> +
>>   static inline int rk_pll_id(enum rk_clk_id clk_id)
>>   {
>>       return clk_id - 1;
>> diff --git a/drivers/clk/rockchip/Makefile 
>> b/drivers/clk/rockchip/Makefile
>> index 41cfb7ad3fd6..03a9fa77babc 100644
>> --- a/drivers/clk/rockchip/Makefile
>> +++ b/drivers/clk/rockchip/Makefile
>> @@ -3,6 +3,7 @@
>>   # Copyright (c) 2017 Rockchip Electronics Co., Ltd
>>   #
>>   +obj-y += clk_pll.o
>>   obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
>>   obj-$(CONFIG_ROCKCHIP_RK3128) += clk_rk3128.o
>>   obj-$(CONFIG_ROCKCHIP_RK3188) += clk_rk3188.o
>> diff --git a/drivers/clk/rockchip/clk_pll.c 
>> b/drivers/clk/rockchip/clk_pll.c
>> new file mode 100644
>> index 000000000000..be0f1245e870
>> --- /dev/null
>> +++ b/drivers/clk/rockchip/clk_pll.c
>> @@ -0,0 +1,361 @@
>> +/*
>> + * (C) Copyright 2018 Rockchip Electronics Co., Ltd
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0
>> + */
>> + #include <common.h>
>> +#include <bitfield.h>
>> +#include <clk-uclass.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <asm/io.h>
>> +#include <asm/arch-rockchip/clock.h>
>> +#include <asm/arch-rockchip/hardware.h>
>> +#include <div64.h>
>> +
>> +static struct rockchip_pll_rate_table rockchip_auto_table;
>> +
>> +#define PLL_MODE_MASK                0x3
>> +#define PLL_RK3328_MODE_MASK            0x1
>> +
>> +#define RK3036_PLLCON0_FBDIV_MASK        0xfff
>> +#define RK3036_PLLCON0_FBDIV_SHIFT        0
>> +#define RK3036_PLLCON0_POSTDIV1_MASK        0x7 << 12
>> +#define RK3036_PLLCON0_POSTDIV1_SHIFT        12
>> +#define RK3036_PLLCON1_REFDIV_MASK        0x3f
>> +#define RK3036_PLLCON1_REFDIV_SHIFT        0
>> +#define RK3036_PLLCON1_POSTDIV2_MASK        0x7 << 6
>> +#define RK3036_PLLCON1_POSTDIV2_SHIFT        6
>> +#define RK3036_PLLCON1_DSMPD_MASK        0x1 << 12
>> +#define RK3036_PLLCON1_DSMPD_SHIFT        12
>> +#define RK3036_PLLCON2_FRAC_MASK        0xffffff
>> +#define RK3036_PLLCON2_FRAC_SHIFT        0
>> +#define RK3036_PLLCON1_PWRDOWN_SHIT        13
>> +
>> +#define MHZ        1000000
>> +#define KHZ        1000
>> +enum {
>> +    OSC_HZ            = 24 * 1000000,
>> +    VCO_MAX_HZ    = 3200U * 1000000,
>> +    VCO_MIN_HZ    = 800 * 1000000,
>> +    OUTPUT_MAX_HZ    = 3200U * 1000000,
>> +    OUTPUT_MIN_HZ    = 24 * 1000000,
>> +};
>> +
>> +#define MIN_FOUTVCO_FREQ    (800 * MHZ)
>> +#define MAX_FOUTVCO_FREQ    (2000 * MHZ)
>> +
>> +int gcd(int m, int n)
>> +{
>> +    int t;
>> +
>> +    while (m > 0) {
>> +        if (n > m) {
>> +            t = m;
>> +            m = n;
>> +            n = t;
>> +        } /* swap */
>> +        m -= n;
>> +    }
>> +    return n;
>> +}
>> +
>> +/*
>> + * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
>> + * Formulas also embedded within the Fractional PLL Verilog model:
>> + * If DSMPD = 1 (DSM is disabled, "integer mode")
>> + * FOUTVCO = FREF / REFDIV * FBDIV
>> + * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
>> + * Where:
>> + * FOUTVCO = Fractional PLL non-divided output frequency
>> + * FOUTPOSTDIV = Fractional PLL divided output frequency
>> + *               (output of second post divider)
>> + * FREF = Fractional PLL input reference frequency, (the OSC_HZ 
>> 24MHz input)
>> + * REFDIV = Fractional PLL input reference clock divider
>> + * FBDIV = Integer value programmed into feedback divide
>> + *
>> + */
>> +
>> +static int rockchip_pll_clk_set_postdiv(ulong fout_hz,
>> +                    u32 *postdiv1,
>> +                    u32 *postdiv2,
>> +                    u32 *foutvco)
>> +{
>> +    ulong freq;
>> +
>> +    if (fout_hz < MIN_FOUTVCO_FREQ) {
>> +        for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
>> +            for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
>> +                freq = fout_hz * (*postdiv1) * (*postdiv2);
>> +                if (freq >= MIN_FOUTVCO_FREQ &&
>> +                    freq <= MAX_FOUTVCO_FREQ) {
>> +                    *foutvco = freq;
>> +                    return 0;
>> +                }
>> +            }
>> +        }
>> +        printf("Can't FIND postdiv1/2 to make fout=%lu in 
>> 800~2000M.\n",
>> +               fout_hz);
>> +    } else {
>> +        *postdiv1 = 1;
>> +        *postdiv2 = 1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static struct rockchip_pll_rate_table *
>> +rockchip_pll_clk_set_by_auto(ulong fin_hz,
>> +                 ulong fout_hz)
>> +{
>> +    struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
>> +    /* FIXME set postdiv1/2 always 1*/
>> +    u32 foutvco = fout_hz;
>> +    ulong fin_64, frac_64;
>> +    u32 f_frac, postdiv1, postdiv2;
>> +    ulong clk_gcd = 0;
>> +
>> +    if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
>> +        return NULL;
>> +
>> +    rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, 
>> &foutvco);
>> +    rate_table->postdiv1 = postdiv1;
>> +    rate_table->postdiv2 = postdiv2;
>> +    rate_table->dsmpd = 1;
>> +
>> +    if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == 
>> fout_hz) {
>> +        fin_hz /= MHZ;
>> +        foutvco /= MHZ;
>> +        clk_gcd = gcd(fin_hz, foutvco);
>> +        rate_table->refdiv = fin_hz / clk_gcd;
>> +        rate_table->fbdiv = foutvco / clk_gcd;
>> +
>> +        rate_table->frac = 0;
>> +
>> +        debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n",
>> +              fin_hz, fout_hz, clk_gcd);
>> +        debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n",
>> +              rate_table->refdiv,
>> +              rate_table->fbdiv, rate_table->postdiv1,
>> +              rate_table->postdiv2);
>> +    } else {
>> +        debug("frac div,fin_hz = %ld,fout_hz = %ld\n",
>> +              fin_hz, fout_hz);
>> +        debug("frac get postdiv1 = %d,  postdiv2 = %d, foutvco = %d\n",
>> +              rate_table->postdiv1, rate_table->postdiv2, foutvco);
>> +        clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
>> +        rate_table->refdiv = fin_hz / MHZ / clk_gcd;
>> +        rate_table->fbdiv = foutvco / MHZ / clk_gcd;
>> +        debug("frac get refdiv = %d,  fbdiv = %d\n",
>> +              rate_table->refdiv, rate_table->fbdiv);
>> +
>> +        rate_table->frac = 0;
>> +
>> +        f_frac = (foutvco % MHZ);
>> +        fin_64 = fin_hz;
>> +        fin_64 = fin_64 / rate_table->refdiv;
>> +        frac_64 = f_frac << 24;
>> +        frac_64 = frac_64 / fin_64;
>> +        rate_table->frac = frac_64;
>> +        if (rate_table->frac > 0)
>> +            rate_table->dsmpd = 0;
>> +        debug("frac = %x\n", rate_table->frac);
>> +    }
>> +    return rate_table;
>> +}
>> +
>> +static const struct rockchip_pll_rate_table *
>> +rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate)
>> +{
>> +    struct rockchip_pll_rate_table  *rate_table = pll->rate_table;
>> +
>> +    while (rate_table->rate) {
>> +        if (rate_table->rate == rate)
>> +            break;
>> +        rate_table++;
>> +    }
>> +    if (rate_table->rate != rate)
>> +        return rockchip_pll_clk_set_by_auto(24 * MHZ, rate);
>> +    else
>> +        return rate_table;
>> +}
>> +
>> +static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
>> +                   void __iomem *base, ulong pll_id,
>> +                   ulong drate)
>> +{
>> +    const struct rockchip_pll_rate_table *rate;
>> +
>> +    rate = rockchip_get_pll_settings(pll, drate);
>> +    if (!rate) {
>> +        printf("%s unsupport rate\n", __func__);
>> +        return -EINVAL;
>> +    }
>> +
>> +    debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, 
>> refdiv: %d\n",
>> +          __func__, rate->rate, rate->fbdiv, rate->postdiv1, 
>> rate->refdiv);
>> +    debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: 
>> %d\n",
>> +          __func__, rate->rate, rate->postdiv2, rate->dsmpd, 
>> rate->frac);
>> +
>> +    /*
>> +     * When power on or changing PLL setting,
>> +     * we must force PLL into slow mode to ensure output stable clock.
>> +     */
>> +    rk_clrsetreg(base + pll->mode_offset,
>> +             pll->mode_mask << pll->mode_shift,
>> +             RKCLK_PLL_MODE_SLOW << pll->mode_shift);
>> +
>> +    /* Power down */
>> +    rk_setreg(base + pll->con_offset + 0x4,
>> +          1 << RK3036_PLLCON1_PWRDOWN_SHIT);
>> +
>> +    rk_clrsetreg(base + pll->con_offset,
>> +             (RK3036_PLLCON0_POSTDIV1_MASK |
>> +             RK3036_PLLCON0_FBDIV_MASK),
>> +             (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) |
>> +             rate->fbdiv);
>> +    rk_clrsetreg(base + pll->con_offset + 0x4,
>> +             (RK3036_PLLCON1_POSTDIV2_MASK |
>> +             RK3036_PLLCON1_REFDIV_MASK),
>> +             (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT |
>> +             rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT));
>> +    if (!rate->dsmpd) {
>> +        rk_clrsetreg(base + pll->con_offset + 0x4,
>> +                 RK3036_PLLCON1_DSMPD_MASK,
>> +                 rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT);
>> +        writel((readl(base + pll->con_offset + 0x8) &
>> +            (~RK3036_PLLCON2_FRAC_MASK)) |
>> +                (rate->frac << RK3036_PLLCON2_FRAC_SHIFT),
>> +                base + pll->con_offset + 0x8);
>> +    }
>> +
>> +    /* Power Up */
>> +    rk_clrreg(base + pll->con_offset + 0x4,
>> +          1 << RK3036_PLLCON1_PWRDOWN_SHIT);
>> +
>> +    /* waiting for pll lock */
>> +    while (!(readl(base + pll->con_offset + 0x4) & (1 << 
>> pll->lock_shift)))
>> +        udelay(1);
>> +
>> +    rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << 
>> pll->mode_shift,
>> +             RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
>> +    debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
>> +          pll, readl(base + pll->con_offset),
>> +          readl(base + pll->con_offset + 0x4),
>> +          readl(base + pll->con_offset + 0x8),
>> +          readl(base + pll->mode_offset));
>> +
>> +    return 0;
>> +}
>> +
>> +static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll,
>> +                 void __iomem *base, ulong pll_id)
>> +{
>> +    u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac;
>> +    u32 con = 0, shift, mask;
>> +    ulong rate;
>> +
>> +    con = readl(base + pll->mode_offset);
>> +    shift = pll->mode_shift;
>> +    mask = pll->mode_mask << shift;
>> +
>> +    switch ((con & mask) >> shift) {
>> +    case RKCLK_PLL_MODE_SLOW:
>> +        return OSC_HZ;
>> +    case RKCLK_PLL_MODE_NORMAL:
>> +        /* normal mode */
>> +        con = readl(base + pll->con_offset);
>> +        postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >>
>> +               RK3036_PLLCON0_POSTDIV1_SHIFT;
>> +        fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >>
>> +            RK3036_PLLCON0_FBDIV_SHIFT;
>> +        con = readl(base + pll->con_offset + 0x4);
>> +        postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >>
>> +               RK3036_PLLCON1_POSTDIV2_SHIFT;
>> +        refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >>
>> +             RK3036_PLLCON1_REFDIV_SHIFT;
>> +        dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >>
>> +            RK3036_PLLCON1_DSMPD_SHIFT;
>> +        con = readl(base + pll->con_offset + 0x8);
>> +        frac = (con & RK3036_PLLCON2_FRAC_MASK) >>
>> +            RK3036_PLLCON2_FRAC_SHIFT;
>> +        rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
>> +        if (dsmpd == 0) {
>> +            u64 frac_rate = OSC_HZ * (u64)frac;
>> +
>> +            do_div(frac_rate, refdiv);
>> +            frac_rate >>= 24;
>> +            do_div(frac_rate, postdiv1);
>> +            do_div(frac_rate, postdiv1);
>> +            rate += frac_rate;
>> +        }
>> +        return rate;
>> +    case RKCLK_PLL_MODE_DEEP:
>> +    default:
>> +        return 32768;
>> +    }
>> +}
>> +
>> +ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll,
>> +                void __iomem *base,
>> +                ulong pll_id)
>> +{
>> +    ulong rate = 0;
>> +
>> +    switch (pll->type) {
>> +    case pll_rk3036:
>> +        pll->mode_mask = PLL_MODE_MASK;
>> +        rate = rk3036_pll_get_rate(pll, base, pll_id);
>> +        break;
>> +    case pll_rk3328:
>> +        pll->mode_mask = PLL_RK3328_MODE_MASK;
>> +        rate = rk3036_pll_get_rate(pll, base, pll_id);
>> +        break;
>> +    default:
>> +        printf("%s: Unknown pll type for pll clk %ld\n",
>> +               __func__, pll_id);
>> +    }
>> +    return rate;
>> +}
>> +
>> +int rockchip_pll_set_rate(struct rockchip_pll_clock *pll,
>> +              void __iomem *base, ulong pll_id,
>> +              ulong drate)
>> +{
>> +    int ret = 0;
>> +
>> +    if (rockchip_pll_get_rate(pll, base, pll_id) == drate)
>> +        return 0;
>> +
>> +    switch (pll->type) {
>> +    case pll_rk3036:
>> +        pll->mode_mask = PLL_MODE_MASK;
>> +        ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
>> +        break;
>> +    case pll_rk3328:
>> +        pll->mode_mask = PLL_RK3328_MODE_MASK;
>> +        ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
>> +        break;
>> +    default:
>> +        printf("%s: Unknown pll type for pll clk %ld\n",
>> +               __func__, pll_id);
>> +    }
>> +    return ret;
>> +}
>> +
>> +const struct rockchip_cpu_rate_table *
>> +rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table,
>> +              ulong rate)
>> +{
>> +    struct rockchip_cpu_rate_table *ps = cpu_table;
>> +
>> +    while (ps->rate) {
>> +        if (ps->rate == rate)
>> +            break;
>> +        ps++;
>> +    }
>> +    if (ps->rate != rate)
>> +        return NULL;
>> +    else
>> +        return ps;
>> +}
>> +
>
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot




More information about the U-Boot mailing list