[U-Boot] [PATCH 9/9] rockchip: rk3188: Add clock driver

Simon Glass sjg at chromium.org
Sun Jul 17 17:48:33 CEST 2016


Hi Heiko,

On 17 July 2016 at 09:33, Heiko Stübner <heiko at sntech.de> wrote:
> Am Sonntag, 17. Juli 2016, 08:13:58 schrieb Simon Glass:
>> Hi Heiko,
>>
>> On 15 July 2016 at 16:17, Heiko Stuebner <heiko at sntech.de> wrote:
>> > Add a driver for setting up and modifying the various PLLs and peripheral
>> > clocks on the RK3188.
>> >
>> > Signed-off-by: Heiko Stuebner <heiko at sntech.de>
>> > ---
>> >
>> >  arch/arm/include/asm/arch-rockchip/cru_rk3188.h | 186 ++++++++++
>> >  drivers/clk/Makefile                            |   1 +
>> >  drivers/clk/clk_rk3188.c                        | 464
>> >  ++++++++++++++++++++++++ 3 files changed, 651 insertions(+)
>> >  create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3188.h
>> >  create mode 100644 drivers/clk/clk_rk3188.c
>>
>> Could you add a patch to move these files into a drivers/clk/rockchip
>> directory?
>
> ok
>
>> > new file mode 100644
>> > index 0000000..4c28393
>> > --- /dev/null
>> > +++ b/drivers/clk/clk_rk3188.c
>> > @@ -0,0 +1,465 @@
>> > +/*
>> > + * (C) Copyright 2015 Google, Inc
>> > + * (C) Copyright 2016 Heiko Stuebner <heiko at sntech.de>
>> > + *
>> > + * SPDX-License-Identifier:    GPL-2.0
>> > + */
>> > +
>> > +#include <common.h>
>> > +#include <clk-uclass.h>
>> > +#include <dm.h>
>> > +#include <errno.h>
>> > +#include <syscon.h>
>> > +#include <asm/io.h>
>> > +#include <asm/arch/clock.h>
>> > +#include <asm/arch/cru_rk3188.h>
>> > +#include <asm/arch/grf_rk3188.h>
>> > +#include <asm/arch/hardware.h>
>> > +#include <dt-bindings/clock/rk3188-cru.h>
>> > +#include <dm/device-internal.h>
>> > +#include <dm/lists.h>
>> > +#include <dm/uclass-internal.h>
>> > +
>> > +DECLARE_GLOBAL_DATA_PTR;
>> > +
>> > +struct rk3188_clk_priv {
>> > +       struct rk3188_grf *grf;
>> > +       struct rk3188_cru *cru;
>> > +       ulong rate;
>> > +       bool has_bwadj;
>> > +};
>> > +
>> > +struct pll_div {
>> > +       u32 nr;
>> > +       u32 nf;
>> > +       u32 no;
>> > +};
>> > +
>> > +enum {
>> > +       VCO_MAX_HZ      = 2200U * 1000000,
>> > +       VCO_MIN_HZ      = 440 * 1000000,
>> > +       OUTPUT_MAX_HZ   = 2200U * 1000000,
>> > +       OUTPUT_MIN_HZ   = 30 * 1000000,
>> > +       FREF_MAX_HZ     = 2200U * 1000000,
>> > +       FREF_MIN_HZ     = 30 * 1000,
>> > +};
>> > +
>> > +enum {
>> > +       /* PLL CON0 */
>> > +       PLL_OD_MASK             = 0x0f,
>> > +
>> > +       /* PLL CON1 */
>> > +       PLL_NF_MASK             = 0x1fff,
>> > +
>> > +       /* PLL CON2 */
>> > +       PLL_BWADJ_MASK          = 0x0fff,
>> > +
>> > +       /* PLL CON3 */
>> > +       PLL_RESET_SHIFT         = 5,
>> > +
>> > +       /* GRF_SOC_STATUS0 */
>> > +       SOCSTS_DPLL_LOCK        = 1 << 5,
>> > +       SOCSTS_APLL_LOCK        = 1 << 6,
>> > +       SOCSTS_CPLL_LOCK        = 1 << 7,
>> > +       SOCSTS_GPLL_LOCK        = 1 << 8,
>> > +};
>> > +
>> > +#define RATE_TO_DIV(input_rate, output_rate) \
>> > +       ((input_rate) / (output_rate) - 1);
>> > +
>> > +#define DIV_TO_RATE(input_rate, div)   ((input_rate) / ((div) + 1))
>> > +
>> > +#define PLL_DIVISORS(hz, _nr, _no) {\
>> > +       .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
>> > +       _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
>> > +                      (_nr * _no) == hz, #hz "Hz cannot be hit with PLL
>> > "\
>> > +                      "divisors on line " __stringify(__LINE__));
>> > +
>> > +/* Keep divisors as low as possible to reduce jitter and power usage */
>> > +static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
>> > +static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
>> > +static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
>> > +
>> > +void *rockchip_get_cru(void)
>> > +{
>> > +       struct rk3188_clk_priv *priv;
>> > +       struct udevice *dev;
>> > +       int ret;
>> > +
>> > +       ret = uclass_get_device_by_name(UCLASS_CLK,
>> > +                                       "clock-controller at 20000000",
>> > &dev);
>>
>> This seems odd. Could you use uclass_get_device(UCLASS_CLK, 0, .&dev) ?
>
> Index 0 actually gets me the 24MHz oscillator fixed clock :-), which is why I
> switched to the by-name variant to not depend on some dts or uclass ordering.
> I'm wondering how that works on the other socs.

I suspect this might have become broken by Stephen's clock changes. I
had a bit of a look at this but have not resolved it yet. For example
on firefly, HDMI does not work now.

There is this:

enum rk_clk_id {
CLK_OSC,
CLK_ARM,
CLK_DDR,
CLK_CODEC,
CLK_GENERAL,
CLK_NEW,

CLK_COUNT,
};

and it used to check against the platform data (see rkclk_get_clk() in
v2016.05). I'll have a think about it.

>
>>
>> > +       if (ret)
>> > +               return ERR_PTR(ret);
>> > +
>> > +       priv = dev_get_priv(dev);
>> > +
>> > +       return priv->cru;
>> > +}
>> > +
>> > +static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id,
>> > +                        const struct pll_div *div, bool has_bwadj)
>> > +{
>> > +       int pll_id = rk_pll_id(clk_id);
>> > +       struct rk3188_pll *pll = &cru->pll[pll_id];
>> > +       /* All PLLs have same VCO and output frequency range restrictions.
>> > */ +       uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
>> > +       uint output_hz = vco_hz / div->no;
>> > +
>> > +       debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
>> > +             (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
>> > +       assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
>> > +              output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
>> > +              (div->no == 1 || !(div->no % 2)));
>> > +
>> > +       /* enter reset */
>> > +       rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
>> > +
>> > +       rk_clrsetreg(&pll->con0,
>> > +                    CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
>> > +                    ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
>> > +       rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
>> > +
>> > +       if (has_bwadj)
>> > +               rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) -
>> > 1); +
>> > +       udelay(10);
>> > +
>> > +       /* return from reset */
>> > +       rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
>> > +
>> > +       return 0;
>> > +}
>> > +
>> > +static inline unsigned int log2(unsigned int value)
>>
>> Hmm this should go in a common file. Perhaps bitfield.h or common.h?
>
> it looks like uboot is already carrying ilog2() in include/linux/log2.h ?

Yes.

>
> [...]
>
>> > +static int rk3188_clk_probe(struct udevice *dev)
>> > +{
>> > +       struct rk3188_clk_priv *priv = dev_get_priv(dev);
>> > +
>> > +       priv->cru = (struct rk3188_cru *)dev_get_addr(dev);
>> > +       priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
>> > +       priv->has_bwadj = of_device_is_compatible(dev,
>> > "rockchip,rk3188a-cru") +                       ? 1 : 0;
>>
>> You should add a .data member to your udevice_id array below using a
>> two-member enum, and check dev_get_driver_data() here.
>
> ah, nice to know. I was wondering if and how uboot was handling .data stuff
> but my short skimming through the sources didn't reveal that. Will change.

I'm AFK for a few hours now.

Regards,
Simon


More information about the U-Boot mailing list