Hi Kever, On 24 July 2016 at 22:50, Kever Yang <kever.y...@rock-chips.com> wrote: > This patch add driver for: > - clock driver including set_rate for cpu, mmc, vop, I2C. > - sysreset driver > - grf syscon driver > > Signed-off-by: Kever Yang <kever.y...@rock-chips.com> > --- > > arch/arm/include/asm/arch-rockchip/cru_rk3399.h | 93 +++ > arch/arm/mach-rockchip/rk3399/Makefile | 2 + > arch/arm/mach-rockchip/rk3399/reset_rk3399.c | 45 ++ > arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | 20 + > drivers/clk/Makefile | 1 + > drivers/clk/clk_rk3399.c | 825 > ++++++++++++++++++++++++ > 6 files changed, 986 insertions(+) > create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3399.h > create mode 100644 arch/arm/mach-rockchip/rk3399/reset_rk3399.c > create mode 100644 arch/arm/mach-rockchip/rk3399/syscon_rk3399.c > create mode 100644 drivers/clk/clk_rk3399.c > > diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3399.h > b/arch/arm/include/asm/arch-rockchip/cru_rk3399.h > new file mode 100644 > index 0000000..c919f47 > --- /dev/null > +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3399.h > @@ -0,0 +1,93 @@ > +/* > + * (C) Copyright 2016 Rockchip Electronics Co., Ltd > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef __ASM_ARCH_CRU_RK3399_H_ > +#define __ASM_ARCH_CRU_RK3399_H_ > + > +#include <common.h> > + > +struct rk3399_pmucru { > + u32 ppll_con[6]; > + u32 reserved[0x1a]; > + u32 pmucru_clksel[6]; > + u32 pmucru_clkfrac_con[2]; > + u32 reserved2[0x18]; > + u32 pmucru_clkgate_con[3]; > + u32 reserved3; > + u32 pmucru_softrst_con[2]; > + u32 reserved4[2]; > + u32 pmucru_rstnhold_con[2]; > + u32 reserved5[2]; > + u32 pmucru_gatedis_con[2]; > +}; > +check_member(rk3399_pmucru, pmucru_gatedis_con[1], 0x134); > + > +struct rk3399_cru { > + u32 apll_l_con[6]; > + u32 reserved[2]; > + u32 apll_b_con[6]; > + u32 reserved1[2]; > + u32 dpll_con[6]; > + u32 reserved2[2]; > + u32 cpll_con[6]; > + u32 reserved3[2]; > + u32 gpll_con[6]; > + u32 reserved4[2]; > + u32 npll_con[6]; > + u32 reserved5[2]; > + u32 vpll_con[6]; > + u32 reserved6[0x0a]; > + u32 clksel_con[108]; > + u32 reserved7[0x14]; > + u32 clkgate_con[35]; > + u32 reserved8[0x1d]; > + u32 softrst_con[21]; > + u32 reserved9[0x2b]; > + u32 glb_srst_fst_value; > + u32 glb_srst_snd_value; > + u32 glb_cnt_th; > + u32 misc_con; > + u32 glb_rst_con; > + u32 glb_rst_st; > + u32 reserved10[0x1a]; > + u32 sdmmc_con[2]; > + u32 sdio0_con[2]; > + u32 sdio1_con[2]; > +}; > +check_member(rk3399_cru, sdio1_con[1], 0x594); > +#define MHz 1000000 > +#define KHz 1000 > +#define OSC_HZ (24*MHz) > +#define APLL_HZ (600*MHz) > +#define GPLL_HZ (594*MHz) > +#define CPLL_HZ (384*MHz) > +#define PPLL_HZ (594*MHz) > + > +#define PMU_PCLK_HZ (99*MHz) > + > +#define ACLKM_CORE_HZ (300*MHz) > +#define ATCLK_CORE_HZ (300*MHz) > +#define PCLK_DBG_HZ (100*MHz) > + > +#define PERIHP_ACLK_HZ (148500*KHz) > +#define PERIHP_HCLK_HZ (148500*KHz) > +#define PERIHP_PCLK_HZ (37125*KHz) > + > +#define PERILP0_ACLK_HZ (99000*KHz) > +#define PERILP0_HCLK_HZ (99000*KHz) > +#define PERILP0_PCLK_HZ (49500*KHz) > + > +#define PERILP1_HCLK_HZ (99000*KHz) > +#define PERILP1_PCLK_HZ (49500*KHz) > + > +#define PWM_CLOCK_HZ PMU_PCLK_HZ > + > +enum apll_l_frequencies { > + APLL_L_1600_MHZ, > + APLL_L_600_MHZ, > +}; > + > +#endif /* __ASM_ARCH_CRU_RK3399_H_ */ > diff --git a/arch/arm/mach-rockchip/rk3399/Makefile > b/arch/arm/mach-rockchip/rk3399/Makefile > index 3f219ac..3ca2028 100644 > --- a/arch/arm/mach-rockchip/rk3399/Makefile > +++ b/arch/arm/mach-rockchip/rk3399/Makefile > @@ -5,3 +5,5 @@ > # > > obj-y += rk3399.o > +obj-y += reset_rk3399.o > +obj-y += syscon_rk3399.o > diff --git a/arch/arm/mach-rockchip/rk3399/reset_rk3399.c > b/arch/arm/mach-rockchip/rk3399/reset_rk3399.c > new file mode 100644 > index 0000000..9a55546 > --- /dev/null > +++ b/arch/arm/mach-rockchip/rk3399/reset_rk3399.c > @@ -0,0 +1,45 @@ > +/* > + * (C) Copyright 2016 Rockchip Electronics Co., Ltd > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <errno.h> > +#include <sysreset.h> > +#include <asm/io.h> > +#include <asm/arch/clock.h> > +#include <asm/arch/cru_rk3399.h> > +#include <asm/arch/hardware.h> > +#include <linux/err.h> > + > +int rk3399_sysreset_request(struct udevice *dev, enum sysreset_t type) > +{ > + struct rk3399_cru *cru = rockchip_get_cru(); > + > + if (IS_ERR(cru)) > + return PTR_ERR(cru); > + switch (type) { > + case SYSRESET_WARM: > + writel(0xeca8, &cru->glb_srst_snd_value); > + break; > + case SYSRESET_COLD: > + writel(0xfdb9, &cru->glb_srst_fst_value); > + break; > + default: > + return -EPROTONOSUPPORT; > + } > + > + return -EINPROGRESS; > +} > + > +static struct sysreset_ops rk3399_sysreset = { > + .request = rk3399_sysreset_request, > +}; > + > +U_BOOT_DRIVER(sysreset_rk3399) = { > + .name = "rk3399_sysreset", > + .id = UCLASS_SYSRESET, > + .ops = &rk3399_sysreset, > +}; > diff --git a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c > b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c > new file mode 100644 > index 0000000..2d81c55 > --- /dev/null > +++ b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c > @@ -0,0 +1,20 @@ > +/* > + * (C) Copyright 2016 Rockchip Electronics Co., Ltd > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <syscon.h> > +#include <asm/arch/clock.h> > + > +static const struct udevice_id rk3399_syscon_ids[] = { > + { .compatible = "rockchip,rk3399-grf", .data = ROCKCHIP_SYSCON_GRF }, > +}; > + > +U_BOOT_DRIVER(syscon_rk3399) = { > + .name = "rk3399_syscon", > + .id = UCLASS_SYSCON, > + .of_match = rk3399_syscon_ids, > +}; > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index f7a8891..e20e932 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -8,6 +8,7 @@ > obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o > obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o > obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o > +obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o > obj-$(CONFIG_SANDBOX) += clk_sandbox.o > obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o > obj-$(CONFIG_MACH_PIC32) += clk_pic32.o > diff --git a/drivers/clk/clk_rk3399.c b/drivers/clk/clk_rk3399.c > new file mode 100644 > index 0000000..6e34c24 > --- /dev/null > +++ b/drivers/clk/clk_rk3399.c > @@ -0,0 +1,825 @@ > +/* > + * (C) Copyright 2015 Google, Inc > + * > + * 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_rk3399.h> > +#include <asm/arch/hardware.h> > +#include <dm/lists.h> > +#include <dt-bindings/clock/rk3399-cru.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct rk3399_clk_priv { > + struct rk3399_cru *cru; > + ulong rate; > +}; > + > +struct pll_div { > + u32 refdiv; > + u32 fbdiv; > + u32 postdiv1; > + u32 postdiv2; > + u32 frac; > +}; > + > +#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, _refdiv, _postdiv1, _postdiv2) {\ > + .refdiv = _refdiv,\ > + .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ > + .postdiv1 = _postdiv1, .postdiv2 = _postdiv2}; > + > +static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); > +static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2, 2); > +static const struct pll_div ppll_init_cfg = PLL_DIVISORS(PPLL_HZ, 2, 2, 1); > + > +static const struct pll_div apll_l_1600_cfg = PLL_DIVISORS(1600*MHz, 3, 1, > 1); > +static const struct pll_div apll_l_600_cfg = PLL_DIVISORS(600*MHz, 1, 2, 1); > + > +static const struct pll_div *apll_l_cfgs[] = { > + [APLL_L_1600_MHZ] = &apll_l_1600_cfg, > + [APLL_L_600_MHZ] = &apll_l_600_cfg, > +}; > + > +enum { > + /* PLL_CON0 */ > + PLL_FBDIV_MASK = 0xfff, > + PLL_FBDIV_SHIFT = 0, > + > + /* PLL_CON1 */ > + PLL_POSTDIV2_MASK = 0x7, > + PLL_POSTDIV2_SHIFT = 12,
I messed this up in my initial patches. Really it should be like this: PLL_POSTDIV2_SHIFT = 12, PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, and then in the C code you don't need to have PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT. You can just use PLL_POSTDIV2_MASK. Can you fix this? It would be good to fix it for other SoCs too. > + PLL_POSTDIV1_MASK = 0x7, > + PLL_POSTDIV1_SHIFT = 8, > + PLL_REFDIV_MASK = 0x3f, > + PLL_REFDIV_SHIFT = 0, > + > + /* PLL_CON2 */ > + PLL_LOCK_STATUS_MASK = 1, > + PLL_LOCK_STATUS_SHIFT = 31, > + PLL_FRACDIV_MASK = 0xffffff, > + PLL_FRACDIV_SHIFT = 0, > + > + /* PLL_CON3 */ > + PLL_MODE_MASK = 3, > + PLL_MODE_SHIFT = 8, > + PLL_MODE_SLOW = 0, > + PLL_MODE_NORM, > + PLL_MODE_DEEP, > + PLL_DSMPD_MASK = 1, > + PLL_DSMPD_SHIFT = 3, > + PLL_INTEGER_MODE = 1, > + > + /* PMUCRU_CLKSEL_CON0 */ > + PMU_PCLK_DIV_CON_MASK = 0x1f, > + PMU_PCLK_DIV_CON_SHIFT = 0, > + > + /* PMUCRU_CLKSEL_CON1 */ > + SPI3_PLL_SEL_MASK = 1, > + SPI3_PLL_SEL_SHIFT = 7, > + SPI3_PLL_SEL_24M = 0, > + SPI3_PLL_SEL_PPLL = 1, > + SPI3_DIV_CON_MASK = 0x7f, > + SPI3_DIV_CON_SHIFT = 0x0, > + > + /* PMUCRU_CLKSEL_CON2 */ > + I2C_DIV_CON_MASK = 0x7f, > + I2C8_DIV_CON_SHIFT = 8, > + I2C0_DIV_CON_SHIFT = 0, > + > + /* PMUCRU_CLKSEL_CON3 */ > + I2C4_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON0 */ > + ACLKM_CORE_L_DIV_CON_MASK = 0x1f, > + ACLKM_CORE_L_DIV_CON_SHIFT = 8, > + CLK_CORE_L_PLL_SEL_MASK = 3, > + CLK_CORE_L_PLL_SEL_SHIFT = 6, > + CLK_CORE_L_PLL_SEL_ALPLL = 0x0, > + CLK_CORE_L_PLL_SEL_ABPLL = 0x1, > + CLK_CORE_L_PLL_SEL_DPLL = 0x10, > + CLK_CORE_L_PLL_SEL_GPLL = 0x11, > + CLK_CORE_L_DIV_MASK = 0x1f, > + CLK_CORE_L_DIV_SHIFT = 0, > + > + /* CLKSEL_CON1 */ > + PCLK_DBG_L_DIV_MASK = 0x1f, > + PCLK_DBG_L_DIV_SHIFT = 0x8, > + ATCLK_CORE_L_DIV_MASK = 0x1f, > + ATCLK_CORE_L_DIV_SHIFT = 0, > + > + /* CLKSEL_CON14 */ > + PCLK_PERIHP_DIV_CON_MASK = 0x7, > + PCLK_PERIHP_DIV_CON_SHIFT = 12, > + HCLK_PERIHP_DIV_CON_MASK = 3, > + HCLK_PERIHP_DIV_CON_SHIFT = 8, > + ACLK_PERIHP_PLL_SEL_MASK = 1, > + ACLK_PERIHP_PLL_SEL_SHIFT = 7, > + ACLK_PERIHP_PLL_SEL_CPLL = 0, > + ACLK_PERIHP_PLL_SEL_GPLL = 1, > + ACLK_PERIHP_DIV_CON_MASK = 0x1f, > + ACLK_PERIHP_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON21 */ > + ACLK_EMMC_PLL_SEL_MASK = 0x1, > + ACLK_EMMC_PLL_SEL_SHIFT = 7, > + ACLK_EMMC_PLL_SEL_GPLL = 0x1, > + ACLK_EMMC_DIV_CON_MASK = 0x1f, > + ACLK_EMMC_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON22 */ > + CLK_EMMC_PLL_MASK = 0x7, > + CLK_EMMC_PLL_SHIFT = 8, > + CLK_EMMC_PLL_SEL_GPLL = 0x1, > + CLK_EMMC_DIV_CON_MASK = 0x7f, > + CLK_EMMC_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON23 */ > + PCLK_PERILP0_DIV_CON_MASK = 0x7, > + PCLK_PERILP0_DIV_CON_SHIFT = 12, > + HCLK_PERILP0_DIV_CON_MASK = 3, > + HCLK_PERILP0_DIV_CON_SHIFT = 8, > + ACLK_PERILP0_PLL_SEL_MASK = 1, > + ACLK_PERILP0_PLL_SEL_SHIFT = 7, > + ACLK_PERILP0_PLL_SEL_CPLL = 0, > + ACLK_PERILP0_PLL_SEL_GPLL = 1, > + ACLK_PERILP0_DIV_CON_MASK = 0x1f, > + ACLK_PERILP0_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON25 */ > + PCLK_PERILP1_DIV_CON_MASK = 0x7, > + PCLK_PERILP1_DIV_CON_SHIFT = 8, > + HCLK_PERILP1_PLL_SEL_MASK = 1, > + HCLK_PERILP1_PLL_SEL_SHIFT = 7, > + HCLK_PERILP1_PLL_SEL_CPLL = 0, > + HCLK_PERILP1_PLL_SEL_GPLL = 1, > + HCLK_PERILP1_DIV_CON_MASK = 0x1f, > + HCLK_PERILP1_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON26 */ > + CLK_SARADC_DIV_CON_MASK = 0xff, > + CLK_SARADC_DIV_CON_SHIFT = 8, > + > + /* CLKSEL_CON27 */ > + CLK_TSADC_SEL_X24M = 0x0, > + CLK_TSADC_SEL_MASK = 1, > + CLK_TSADC_SEL_SHIFT = 15, > + CLK_TSADC_DIV_CON_MASK = 0x3ff, > + CLK_TSADC_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON47 & CLKSEL_CON48 */ > + ACLK_VOP_PLL_SEL_MASK = 0x3, > + ACLK_VOP_PLL_SEL_SHIFT = 6, > + ACLK_VOP_PLL_SEL_CPLL = 0x1, > + ACLK_VOP_DIV_CON_MASK = 0x1f, > + ACLK_VOP_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON49 & CLKSEL_CON50 */ > + DCLK_VOP_DCLK_SEL_MASK = 1, > + DCLK_VOP_DCLK_SEL_SHIFT = 11, > + DCLK_VOP_DCLK_SEL_DIVOUT = 0, > + DCLK_VOP_PLL_SEL_MASK = 3, > + DCLK_VOP_PLL_SEL_SHIFT = 8, > + DCLK_VOP_PLL_SEL_VPLL = 0, > + DCLK_VOP_DIV_CON_MASK = 0xff, > + DCLK_VOP_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON58 */ > + CLK_SPI_PLL_SEL_MASK = 1, > + CLK_SPI_PLL_SEL_CPLL = 0, > + CLK_SPI_PLL_SEL_GPLL = 1, > + CLK_SPI_PLL_DIV_CON_MASK = 0x7f, > + CLK_SPI5_PLL_DIV_CON_SHIFT = 8, > + CLK_SPI5_PLL_SEL_SHIFT = 15, > + > + /* CLKSEL_CON59 */ > + CLK_SPI1_PLL_SEL_SHIFT = 15, > + CLK_SPI1_PLL_DIV_CON_SHIFT = 8, > + CLK_SPI0_PLL_SEL_SHIFT = 7, > + CLK_SPI0_PLL_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON60 */ > + CLK_SPI4_PLL_SEL_SHIFT = 15, > + CLK_SPI4_PLL_DIV_CON_SHIFT = 8, > + CLK_SPI2_PLL_SEL_SHIFT = 7, > + CLK_SPI2_PLL_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON61 */ > + CLK_I2C_PLL_SEL_MASK = 1, > + CLK_I2C_PLL_SEL_CPLL = 0, > + CLK_I2C_PLL_SEL_GPLL = 1, > + CLK_I2C5_PLL_SEL_SHIFT = 15, > + CLK_I2C5_DIV_CON_SHIFT = 8, > + CLK_I2C1_PLL_SEL_SHIFT = 7, > + CLK_I2C1_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON62 */ > + CLK_I2C6_PLL_SEL_SHIFT = 15, > + CLK_I2C6_DIV_CON_SHIFT = 8, > + CLK_I2C2_PLL_SEL_SHIFT = 7, > + CLK_I2C2_DIV_CON_SHIFT = 0, > + > + /* CLKSEL_CON63 */ > + CLK_I2C7_PLL_SEL_SHIFT = 15, > + CLK_I2C7_DIV_CON_SHIFT = 8, > + CLK_I2C3_PLL_SEL_SHIFT = 7, > + CLK_I2C3_DIV_CON_SHIFT = 0, > + > + /* CRU_SOFTRST_CON4 */ > + RESETN_DDR0_REQ_MASK = 1, > + RESETN_DDR0_REQ_SHIFT = 8, > + RESETN_DDRPHY0_REQ_MASK = 1, > + RESETN_DDRPHY0_REQ_SHIFT = 9, > + RESETN_DDR1_REQ_MASK = 1, > + RESETN_DDR1_REQ_SHIFT = 12, > + RESETN_DDRPHY1_REQ_MASK = 1, > + RESETN_DDRPHY1_REQ_SHIFT = 13, > +}; > + > +#define VCO_MAX_KHZ (3200 * (MHz / KHz)) > +#define VCO_MIN_KHZ (800 * (MHz / KHz)) > +#define OUTPUT_MAX_KHZ (3200 * (MHz / KHz)) > +#define OUTPUT_MIN_KHZ (16 * (MHz / KHz)) > + > +/* the div restrictions of pll in integer mode, comment style need fixing throughout /* * The div restructions Also please try to wrap comments to around 75 columns. > + * these are defined in * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0 > + */ > +#define PLL_DIV_MIN 16 > +#define PLL_DIV_MAX 3200 > + > +/* 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 void rkclk_set_pll(u32 *pll_con, const struct pll_div *div) > +{ > + /* All 8 PLLs have same VCO and output frequency range restrictions. > */ > + u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv; > + u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2; Why do you use u32 for all these types? Is uint not what you want? > + > + debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, " > + "postdiv2=%d, vco=%u khz, output=%u khz\n", > + pll_con, div->fbdiv, div->refdiv, div->postdiv1, > + div->postdiv2, vco_khz, output_khz); > + assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ && > + output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ && > + div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX); > + > + /* When power on or changing PLL setting, > + * we must force PLL into slow mode to ensure output stable clock. > + */ > + rk_clrsetreg(&pll_con[3], PLL_MODE_MASK << PLL_MODE_SHIFT, > + PLL_MODE_SLOW << PLL_MODE_SHIFT); > + > + /* use integer mode */ > + rk_clrsetreg(&pll_con[3], PLL_DSMPD_MASK << PLL_DSMPD_SHIFT, > + PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); > + > + rk_clrsetreg(&pll_con[0], PLL_FBDIV_MASK << PLL_FBDIV_SHIFT, > + div->fbdiv << PLL_FBDIV_SHIFT); > + rk_clrsetreg(&pll_con[1], > + PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT | > + PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | > + PLL_REFDIV_MASK | PLL_REFDIV_SHIFT, > + (div->postdiv2 << PLL_POSTDIV2_SHIFT) | > + (div->postdiv1 << PLL_POSTDIV1_SHIFT) | > + (div->refdiv << PLL_REFDIV_SHIFT)); > + > + /* waiting for pll lock */ > + while (!(readl(&pll_con[2]) & (1 << PLL_LOCK_STATUS_SHIFT))) > + udelay(1); > + > + /* pll enter normal mode */ > + rk_clrsetreg(&pll_con[3], PLL_MODE_MASK << PLL_MODE_SHIFT, > + PLL_MODE_NORM << PLL_MODE_SHIFT); > +} > + > +static inline unsigned long div_round_up(unsigned int n, unsigned int d) > +{ > + return (n + d - 1) / d; Can you use DIV_ROUND_UP()? > +} > + > +static int pll_para_config(u32 freq_hz, struct pll_div *div) > +{ > + u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0; > + u32 postdiv1, postdiv2 = 1; > + u32 fref_khz; > + u32 diff_khz, best_diff_khz; > + const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16; > + const u32 max_postdiv1 = 7, max_postdiv2 = 7; > + u32 vco_khz; > + u32 freq_khz = freq_hz / KHz; > + > + if (!freq_hz) { > + printf("%s: the frequency can't be 0 Hz\n", __func__); > + return -1; > + } > + > + postdiv1 = div_round_up(VCO_MIN_KHZ, freq_khz); > + if (postdiv1 > max_postdiv1) { > + postdiv2 = div_round_up(postdiv1, max_postdiv1); > + postdiv1 = div_round_up(postdiv1, postdiv2); > + } > + > + vco_khz = freq_khz * postdiv1 * postdiv2; > + > + if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || > + postdiv2 > max_postdiv2) { > + printf("%s: Cannot find out a supported VCO" > + " for Frequency (%uHz).\n", __func__, freq_hz); > + return -1; > + } > + > + div->postdiv1 = postdiv1; > + div->postdiv2 = postdiv2; > + > + best_diff_khz = vco_khz; > + for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) { > + fref_khz = ref_khz / refdiv; > + > + fbdiv = vco_khz / fref_khz; > + if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv)) > + continue; > + diff_khz = vco_khz - fbdiv * fref_khz; > + if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) { > + fbdiv++; > + diff_khz = fref_khz - diff_khz; > + } > + > + if (diff_khz >= best_diff_khz) > + continue; > + > + best_diff_khz = diff_khz; > + div->refdiv = refdiv; > + div->fbdiv = fbdiv; > + } > + > + if (best_diff_khz > 4 * (MHz/KHz)) { > + printf("%s: Failed to match output frequency %u, " > + "difference is %u Hz,exceed 4MHZ\n", __func__, freq_hz, > + best_diff_khz * KHz); > + return -1; > + } > + return 0; > +} > + > +static void rkclk_init(struct rk3399_cru *cru) > +{ > + u32 aclk_div; > + u32 hclk_div; > + u32 pclk_div; > + > + /* some cru registers changed by bootrom, we'd better reset them to > + * reset/default values described in TRM to avoid confusion in kernel. > + * Please consider these threee lines as a fix of bootrom bug. three > + */ > + rk_clrsetreg(&cru->clksel_con[12], 0xffff, 0x4101); > + rk_clrsetreg(&cru->clksel_con[19], 0xffff, 0x033f); > + rk_clrsetreg(&cru->clksel_con[56], 0x0003, 0x0003); > + > + /* configure gpll cpll */ > + rkclk_set_pll(&cru->gpll_con[0], &gpll_init_cfg); > + rkclk_set_pll(&cru->cpll_con[0], &cpll_init_cfg); > + > + /* configure perihp aclk, hclk, pclk */ > + aclk_div = GPLL_HZ / PERIHP_ACLK_HZ - 1; > + assert((aclk_div + 1) * PERIHP_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); > + > + hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1; > + assert((hclk_div + 1) * PERIHP_HCLK_HZ == > + PERIHP_ACLK_HZ && (hclk_div < 0x4)); > + > + pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1; > + assert((pclk_div + 1) * PERIHP_PCLK_HZ == > + PERIHP_ACLK_HZ && (pclk_div < 0x7)); > + > + rk_clrsetreg(&cru->clksel_con[14], > + PCLK_PERIHP_DIV_CON_MASK << PCLK_PERIHP_DIV_CON_SHIFT | > + HCLK_PERIHP_DIV_CON_MASK << HCLK_PERIHP_DIV_CON_SHIFT | > + ACLK_PERIHP_PLL_SEL_MASK << ACLK_PERIHP_PLL_SEL_SHIFT | > + ACLK_PERIHP_DIV_CON_MASK << ACLK_PERIHP_DIV_CON_SHIFT, > + pclk_div << PCLK_PERIHP_DIV_CON_SHIFT | > + hclk_div << HCLK_PERIHP_DIV_CON_SHIFT | > + ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT | > + aclk_div << ACLK_PERIHP_DIV_CON_SHIFT); > + > + /* configure perilp0 aclk, hclk, pclk */ > + aclk_div = GPLL_HZ / PERILP0_ACLK_HZ - 1; > + assert((aclk_div + 1) * PERILP0_ACLK_HZ == GPLL_HZ && aclk_div < > 0x1f); > + > + hclk_div = PERILP0_ACLK_HZ / PERILP0_HCLK_HZ - 1; > + assert((hclk_div + 1) * PERILP0_HCLK_HZ == > + PERILP0_ACLK_HZ && (hclk_div < 0x4)); > + > + pclk_div = PERILP0_ACLK_HZ / PERILP0_PCLK_HZ - 1; > + assert((pclk_div + 1) * PERILP0_PCLK_HZ == > + PERILP0_ACLK_HZ && (pclk_div < 0x7)); > + > + rk_clrsetreg(&cru->clksel_con[23], > + PCLK_PERILP0_DIV_CON_MASK << PCLK_PERILP0_DIV_CON_SHIFT | > + HCLK_PERILP0_DIV_CON_MASK << HCLK_PERILP0_DIV_CON_SHIFT | > + ACLK_PERILP0_PLL_SEL_MASK << ACLK_PERILP0_PLL_SEL_SHIFT | > + ACLK_PERILP0_DIV_CON_MASK << ACLK_PERILP0_DIV_CON_SHIFT, > + pclk_div << PCLK_PERILP0_DIV_CON_SHIFT | > + hclk_div << HCLK_PERILP0_DIV_CON_SHIFT | > + ACLK_PERILP0_PLL_SEL_GPLL << ACLK_PERILP0_PLL_SEL_SHIFT | > + aclk_div << ACLK_PERILP0_DIV_CON_SHIFT); > + > + /* perilp1 hclk select gpll as source */ > + hclk_div = GPLL_HZ / PERILP1_HCLK_HZ - 1; > + assert((hclk_div + 1) * PERILP1_HCLK_HZ == > + GPLL_HZ && (hclk_div < 0x1f)); > + > + pclk_div = PERILP1_HCLK_HZ / PERILP1_HCLK_HZ - 1; > + assert((pclk_div + 1) * PERILP1_HCLK_HZ == > + PERILP1_HCLK_HZ && (hclk_div < 0x7)); > + > + rk_clrsetreg(&cru->clksel_con[25], > + PCLK_PERILP1_DIV_CON_MASK << PCLK_PERILP1_DIV_CON_SHIFT | > + HCLK_PERILP1_DIV_CON_MASK << HCLK_PERILP1_DIV_CON_SHIFT | > + HCLK_PERILP1_PLL_SEL_MASK << HCLK_PERILP1_PLL_SEL_SHIFT, > + pclk_div << PCLK_PERILP1_DIV_CON_SHIFT | > + hclk_div << HCLK_PERILP1_DIV_CON_SHIFT | > + HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT); > +} > + > +void rk3399_configure_cpu(struct rk3399_cru *cru, > + enum apll_l_frequencies apll_l_freq) > +{ > + u32 aclkm_div; > + u32 pclk_dbg_div; > + u32 atclk_div; > + > + rkclk_set_pll(&cru->apll_l_con[0], apll_l_cfgs[apll_l_freq]); > + > + aclkm_div = APLL_HZ / ACLKM_CORE_HZ - 1; > + assert((aclkm_div + 1) * ACLKM_CORE_HZ == APLL_HZ && > + aclkm_div < 0x1f); > + > + pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ - 1; > + assert((pclk_dbg_div + 1) * PCLK_DBG_HZ == APLL_HZ && > + pclk_dbg_div < 0x1f); > + > + atclk_div = APLL_HZ / ATCLK_CORE_HZ - 1; > + assert((atclk_div + 1) * ATCLK_CORE_HZ == APLL_HZ && > + atclk_div < 0x1f); > + > + rk_clrsetreg(&cru->clksel_con[0], > + ACLKM_CORE_L_DIV_CON_MASK << ACLKM_CORE_L_DIV_CON_SHIFT | > + CLK_CORE_L_PLL_SEL_MASK << CLK_CORE_L_PLL_SEL_SHIFT | > + CLK_CORE_L_DIV_MASK << CLK_CORE_L_DIV_SHIFT, > + aclkm_div << ACLKM_CORE_L_DIV_CON_SHIFT | > + CLK_CORE_L_PLL_SEL_ALPLL << CLK_CORE_L_PLL_SEL_SHIFT | > + 0 << CLK_CORE_L_DIV_SHIFT); > + > + rk_clrsetreg(&cru->clksel_con[1], > + PCLK_DBG_L_DIV_MASK << PCLK_DBG_L_DIV_SHIFT | > + ATCLK_CORE_L_DIV_MASK << ATCLK_CORE_L_DIV_SHIFT, > + pclk_dbg_div << PCLK_DBG_L_DIV_SHIFT | > + atclk_div << ATCLK_CORE_L_DIV_SHIFT); > +} > +#define I2C_CLK_REG_MASK(bus) \ > + (I2C_DIV_CON_MASK << \ > + CLK_I2C ##bus## _DIV_CON_SHIFT | \ > + CLK_I2C_PLL_SEL_MASK << \ > + CLK_I2C ##bus## _PLL_SEL_SHIFT) > + > +#define I2C_CLK_REG_VALUE(bus, clk_div) \ > + ((clk_div - 1) << \ > + CLK_I2C ##bus## _DIV_CON_SHIFT | \ > + CLK_I2C_PLL_SEL_GPLL << \ > + CLK_I2C ##bus## _PLL_SEL_SHIFT) > + > +#define I2C_CLK_DIV_VALUE(con, bus) \ > + (con >> CLK_I2C ##bus## _DIV_CON_SHIFT) & \ > + I2C_DIV_CON_MASK; > + > +static ulong rk3399_i2c_get_clk(struct rk3399_cru *cru, ulong clk_id) > +{ > + u32 div, con; > + > + switch (clk_id) { > + case SCLK_I2C1: > + con = readl(&cru->clksel_con[61]); > + div = I2C_CLK_DIV_VALUE(con, 1); > + break; > + case SCLK_I2C2: > + con = readl(&cru->clksel_con[62]); > + div = I2C_CLK_DIV_VALUE(con, 2); > + break; > + case SCLK_I2C3: > + con = readl(&cru->clksel_con[63]); > + div = I2C_CLK_DIV_VALUE(con, 3); > + break; > + case SCLK_I2C5: > + con = readl(&cru->clksel_con[61]); > + div = I2C_CLK_DIV_VALUE(con, 5); > + break; > + case SCLK_I2C6: > + con = readl(&cru->clksel_con[62]); > + div = I2C_CLK_DIV_VALUE(con, 6); > + break; > + case SCLK_I2C7: > + con = readl(&cru->clksel_con[63]); > + div = I2C_CLK_DIV_VALUE(con, 7); > + break; > + default: > + printf("do not support this i2c bus\n"); > + return -EINVAL; > + } > + > + return DIV_TO_RATE(GPLL_HZ, div); > +} > + > +static ulong rk3399_i2c_set_clk(struct rk3399_cru *cru, ulong clk_id, uint > hz) > +{ > + int src_clk_div; > + > + /* i2c0,4,8 src clock from ppll, i2c1,2,3,5,6,7 src clock from gpll*/ > + src_clk_div = GPLL_HZ / hz; > + assert(src_clk_div - 1 < 127); > + > + switch (clk_id) { > + case SCLK_I2C1: > + rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(1), > + I2C_CLK_REG_VALUE(1, src_clk_div)); > + break; > + case SCLK_I2C2: > + rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(2), > + I2C_CLK_REG_VALUE(2, src_clk_div)); > + break; > + case SCLK_I2C3: > + rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(3), > + I2C_CLK_REG_VALUE(3, src_clk_div)); > + break; > + case SCLK_I2C5: > + rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(5), > + I2C_CLK_REG_VALUE(5, src_clk_div)); > + break; > + case SCLK_I2C6: > + rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(6), > + I2C_CLK_REG_VALUE(6, src_clk_div)); > + break; > + case SCLK_I2C7: > + rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(7), > + I2C_CLK_REG_VALUE(7, src_clk_div)); > + break; > + default: > + printf("do not support this i2c bus\n"); > + return -EINVAL; > + } > + > + return DIV_TO_RATE(GPLL_HZ, src_clk_div); > +} > + > +static ulong rk3399_vop_set_clk(struct rk3399_cru *cru, ulong clk_id, u32 hz) > +{ > + struct pll_div vpll_config = {0}; > + int aclk_vop = 198*MHz; > + void *aclkreg_addr, *dclkreg_addr; > + u32 div; > + > + switch (clk_id) { > + case DCLK_VOP0: > + aclkreg_addr = &cru->clksel_con[47]; > + dclkreg_addr = &cru->clksel_con[49]; > + break; > + case DCLK_VOP1: > + aclkreg_addr = &cru->clksel_con[48]; > + dclkreg_addr = &cru->clksel_con[50]; > + break; > + default: > + return -EINVAL; > + } > + /* vop aclk source clk: cpll */ > + div = CPLL_HZ / aclk_vop; > + assert(div - 1 < 32); > + > + rk_clrsetreg(aclkreg_addr, > + ACLK_VOP_PLL_SEL_MASK << ACLK_VOP_PLL_SEL_SHIFT | > + ACLK_VOP_DIV_CON_MASK << ACLK_VOP_DIV_CON_SHIFT, > + ACLK_VOP_PLL_SEL_CPLL << ACLK_VOP_PLL_SEL_SHIFT | > + (div - 1) << ACLK_VOP_DIV_CON_SHIFT); > + > + /* vop dclk source from vpll, and equals to vpll(means div == 1) */ > + if (pll_para_config(hz, &vpll_config)) > + return -1; > + > + rkclk_set_pll(&cru->vpll_con[0], &vpll_config); > + > + rk_clrsetreg(dclkreg_addr, > + DCLK_VOP_DCLK_SEL_MASK << DCLK_VOP_DCLK_SEL_SHIFT | > + DCLK_VOP_PLL_SEL_MASK << DCLK_VOP_PLL_SEL_SHIFT | > + DCLK_VOP_DIV_CON_MASK << DCLK_VOP_DIV_CON_SHIFT, > + DCLK_VOP_DCLK_SEL_DIVOUT << DCLK_VOP_DCLK_SEL_SHIFT | > + DCLK_VOP_PLL_SEL_VPLL << DCLK_VOP_PLL_SEL_SHIFT | > + (1 - 1) << DCLK_VOP_DIV_CON_SHIFT); > + > + return hz; > +} > + > +static ulong rk3399_mmc_get_clk(struct rk3399_cru *cru, uint clk_id) > +{ > + u32 div, con; > + > + switch (clk_id) { > + case SCLK_SDMMC: > + con = readl(&cru->clksel_con[16]); > + break; > + case SCLK_EMMC: > + con = readl(&cru->clksel_con[21]); > + break; > + default: > + return -EINVAL; > + } > + div = (con>>CLK_EMMC_DIV_CON_SHIFT) & CLK_EMMC_DIV_CON_MASK; > + > + return DIV_TO_RATE(GPLL_HZ, div); > +} > + > +static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, > + ulong clk_id, ulong set_rate) > +{ > + int src_clk_div; > + int aclk_emmc = 198*MHz; > + > + switch (clk_id) { > + case SCLK_SDMMC: > + /* Select clk_sdmmc source from GPLL too */ > + src_clk_div = GPLL_HZ / set_rate; > + assert(src_clk_div - 1 < 127); > + > + rk_clrsetreg(&cru->clksel_con[16], > + CLK_EMMC_PLL_MASK << CLK_EMMC_PLL_SHIFT | > + CLK_EMMC_DIV_CON_MASK << CLK_EMMC_DIV_CON_SHIFT, > + CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT | > + (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); > + break; > + case SCLK_EMMC: > + /* Select aclk_emmc source from GPLL */ > + src_clk_div = GPLL_HZ / aclk_emmc; > + assert(src_clk_div - 1 < 31); > + > + rk_clrsetreg(&cru->clksel_con[21], > + ACLK_EMMC_PLL_SEL_MASK << > ACLK_EMMC_PLL_SEL_SHIFT | > + ACLK_EMMC_DIV_CON_MASK << > ACLK_EMMC_DIV_CON_SHIFT, > + ACLK_EMMC_PLL_SEL_GPLL << > ACLK_EMMC_PLL_SEL_SHIFT | > + (src_clk_div - 1) << ACLK_EMMC_DIV_CON_SHIFT); > + > + /* Select clk_emmc source from GPLL too */ > + src_clk_div = GPLL_HZ / set_rate; > + assert(src_clk_div - 1 < 127); > + > + rk_clrsetreg(&cru->clksel_con[22], > + CLK_EMMC_PLL_MASK << CLK_EMMC_PLL_SHIFT | > + CLK_EMMC_DIV_CON_MASK << CLK_EMMC_DIV_CON_SHIFT, > + CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT | > + (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); > + break; > + default: > + return -EINVAL; > + } > + return rk3399_mmc_get_clk(cru, clk_id); > +} > + > +static ulong rk3399_clk_get_rate(struct clk *clk) > +{ > + struct rk3399_clk_priv *priv = dev_get_priv(clk->dev); > + ulong rate = 0; > + > + switch (clk->id) { > + case 0 ... 63: > + return 0; > + case SCLK_SDMMC: > + case SCLK_EMMC: > + rate = rk3399_mmc_get_clk(priv->cru, clk->id); > + break; > + case SCLK_I2C1: > + case SCLK_I2C2: > + case SCLK_I2C3: > + case SCLK_I2C5: > + case SCLK_I2C6: > + case SCLK_I2C7: > + rate = rk3399_i2c_get_clk(priv->cru, clk->id); > + break; > + case DCLK_VOP0: > + case DCLK_VOP1: > + break; > + default: > + return -ENOENT; > + } > + > + return rate; > +} > + > +static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate) > +{ > + struct rk3399_clk_priv *priv = dev_get_priv(clk->dev); > + > + switch (clk->id) { > + case 0 ... 63: > + return 0; > + case SCLK_SDMMC: > + case SCLK_EMMC: > + rk3399_mmc_set_clk(priv->cru, clk->id, rate); > + break; > + case SCLK_I2C1: > + case SCLK_I2C2: > + case SCLK_I2C3: > + case SCLK_I2C5: > + case SCLK_I2C6: > + case SCLK_I2C7: > + rk3399_i2c_set_clk(priv->cru, clk->id, rate); > + break; > + case DCLK_VOP0: > + case DCLK_VOP1: > + rk3399_vop_set_clk(priv->cru, clk->id, rate); > + break; > + default: > + return -ENOENT; > + } > + > + return 0; > +} > + > +static struct clk_ops rk3399_clk_ops = { > + .get_rate = rk3399_clk_get_rate, > + .set_rate = rk3399_clk_set_rate, > +}; > + > +void *rockchip_get_cru(void) > +{ > + struct udevice *dev; > + fdt_addr_t addr; > + int ret; > + > + ret = uclass_get_device_by_name(UCLASS_CLK, > + "clock-controller@ff760000", > + &dev); Can you use uclass_get_device_by_driver()? > + if (ret) > + return ERR_PTR(ret); > + > + addr = dev_get_addr(dev); dev_get_addr_ptr() > + if (addr == FDT_ADDR_T_NONE) > + return ERR_PTR(-EINVAL); > + > + return (void *)addr; > +} > + > +static int rk3399_clk_probe(struct udevice *dev) > +{ > + struct rk3399_clk_priv *priv = dev_get_priv(dev); > + > + priv->cru = (struct rk3399_cru *)dev_get_addr(dev); That should really go in and ofdata_to_platdata() method. > + rkclk_init(priv->cru); > + > + return 0; > +} > + > +static int rk3399_clk_bind(struct udevice *dev) > +{ > + int ret; > + > + /* The reset driver does not have a device node, so bind it here */ > + ret = device_bind_driver(gd->dm_root, "rk3399_sysreset", "reset", > &dev); > + if (ret) > + printf("Warning: No RK3399 reset driver: ret=%d\n", ret); > + > + return 0; > +} > + > +static const struct udevice_id rk3399_clk_ids[] = { > + { .compatible = "rockchip,rk3399-cru" }, > + { } > +}; > + > +U_BOOT_DRIVER(clk_rk3399) = { > + .name = "clk_rk3399", > + .id = UCLASS_CLK, > + .of_match = rk3399_clk_ids, > + .priv_auto_alloc_size = sizeof(struct rk3399_clk_priv), > + .ops = &rk3399_clk_ops, > + .bind = rk3399_clk_bind, > + .probe = rk3399_clk_probe, > +}; > -- > 1.9.1 > > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot