From aff8795c01c39318eb07b4bf0cd7f170ff67d591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Fri, 29 Jul 2016 14:47:21 +0200 Subject: move: rockchip: move clock drivers into a subdirectory With the number of Rockchip clock drivers increasing, don't clutter up the core drivers/clk directory with them and instead move them out of the way into a separate subdirectory. Suggested-by: Simon Glass Signed-off-by: Heiko Stuebner Acked-by: Simon Glass Updated for rk3399: Signed-off-by: Simon Glass diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e20e932..3cbdd54 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -6,9 +6,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_ARCH_ROCKCHIP) += rockchip/ 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_rk3036.c b/drivers/clk/clk_rk3036.c deleted file mode 100644 index 6202c9d..0000000 --- a/drivers/clk/clk_rk3036.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * (C) Copyright 2015 Google, Inc - * - * SPDX-License-Identifier: GPL-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -struct rk3036_clk_priv { - struct rk3036_cru *cru; - ulong rate; -}; - -enum { - VCO_MAX_HZ = 2400U * 1000000, - VCO_MIN_HZ = 600 * 1000000, - OUTPUT_MAX_HZ = 2400U * 1000000, - OUTPUT_MIN_HZ = 24 * 1000000, -}; - -#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_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\ - OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\ - #hz "Hz cannot be hit with PLL "\ - "divisors on line " __stringify(__LINE__)); - -/* use interge mode*/ -static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); -static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); - -static inline unsigned int log2(unsigned int value) -{ - return fls(value) - 1; -} - -void *rockchip_get_cru(void) -{ - struct udevice *dev; - fdt_addr_t addr; - int ret; - - ret = uclass_get_device(UCLASS_CLK, 0, &dev); - if (ret) - return ERR_PTR(ret); - - addr = dev_get_addr(dev); - if (addr == FDT_ADDR_T_NONE) - return ERR_PTR(-EINVAL); - - return (void *)addr; -} - -static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id, - const struct pll_div *div) -{ - int pll_id = rk_pll_id(clk_id); - struct rk3036_pll *pll = &cru->pll[pll_id]; - - /* All PLLs have same VCO and output frequency range restrictions. */ - uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; - uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; - - debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\ - vco=%u Hz, output=%u Hz\n", - pll, div->fbdiv, div->refdiv, div->postdiv1, - div->postdiv2, 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); - - /* use interger mode */ - rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); - - rk_clrsetreg(&pll->con0, - PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK, - (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv); - rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT | - PLL_REFDIV_MASK << PLL_REFDIV_SHIFT, - (div->postdiv2 << PLL_POSTDIV2_SHIFT | - div->refdiv << PLL_REFDIV_SHIFT)); - - /* waiting for pll lock */ - while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)) - udelay(1); - - return 0; -} - -static void rkclk_init(struct rk3036_cru *cru) -{ - u32 aclk_div; - u32 hclk_div; - u32 pclk_div; - - /* pll enter slow-mode */ - rk_clrsetreg(&cru->cru_mode_con, - GPLL_MODE_MASK << GPLL_MODE_SHIFT | - APLL_MODE_MASK << APLL_MODE_SHIFT, - GPLL_MODE_SLOW << GPLL_MODE_SHIFT | - APLL_MODE_SLOW << APLL_MODE_SHIFT); - - /* init pll */ - rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); - rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); - - /* - * select apll as core clock pll source and - * set up dependent divisors for PCLK/HCLK and ACLK clocks. - * core hz : apll = 1:1 - */ - aclk_div = APLL_HZ / CORE_ACLK_HZ - 1; - assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7); - - pclk_div = APLL_HZ / CORE_PERI_HZ - 1; - assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf); - - rk_clrsetreg(&cru->cru_clksel_con[0], - CORE_CLK_PLL_SEL_MASK << CORE_CLK_PLL_SEL_SHIFT | - CORE_DIV_CON_MASK << CORE_DIV_CON_SHIFT, - CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | - 0 << CORE_DIV_CON_SHIFT); - - rk_clrsetreg(&cru->cru_clksel_con[1], - CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT | - CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT, - aclk_div << CORE_ACLK_DIV_SHIFT | - pclk_div << CORE_PERI_DIV_SHIFT); - - /* - * select apll as cpu clock pll source and - * set up dependent divisors for PCLK/HCLK and ACLK clocks. - */ - aclk_div = APLL_HZ / CPU_ACLK_HZ - 1; - assert((aclk_div + 1) * CPU_ACLK_HZ == APLL_HZ && aclk_div < 0x1f); - - pclk_div = APLL_HZ / CPU_PCLK_HZ - 1; - assert((pclk_div + 1) * CPU_PCLK_HZ == APLL_HZ && pclk_div < 0x7); - - hclk_div = APLL_HZ / CPU_HCLK_HZ - 1; - assert((hclk_div + 1) * CPU_HCLK_HZ == APLL_HZ && hclk_div < 0x3); - - rk_clrsetreg(&cru->cru_clksel_con[0], - CPU_CLK_PLL_SEL_MASK << CPU_CLK_PLL_SEL_SHIFT | - ACLK_CPU_DIV_MASK << ACLK_CPU_DIV_SHIFT, - CPU_CLK_PLL_SEL_APLL << CPU_CLK_PLL_SEL_SHIFT | - aclk_div << ACLK_CPU_DIV_SHIFT); - - rk_clrsetreg(&cru->cru_clksel_con[1], - CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT | - CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT, - pclk_div << CPU_PCLK_DIV_SHIFT | - hclk_div << CPU_HCLK_DIV_SHIFT); - - /* - * select gpll as peri clock pll source and - * set up dependent divisors for PCLK/HCLK and ACLK clocks. - */ - aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; - assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); - - hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); - assert((1 << hclk_div) * PERI_HCLK_HZ == - PERI_ACLK_HZ && (pclk_div < 0x4)); - - pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); - assert((1 << pclk_div) * PERI_PCLK_HZ == - PERI_ACLK_HZ && pclk_div < 0x8); - - rk_clrsetreg(&cru->cru_clksel_con[10], - PERI_PLL_SEL_MASK << PERI_PLL_SEL_SHIFT | - PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | - PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | - PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, - PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | - pclk_div << PERI_PCLK_DIV_SHIFT | - hclk_div << PERI_HCLK_DIV_SHIFT | - aclk_div << PERI_ACLK_DIV_SHIFT); - - /* PLL enter normal-mode */ - rk_clrsetreg(&cru->cru_mode_con, - GPLL_MODE_MASK << GPLL_MODE_SHIFT | - APLL_MODE_MASK << APLL_MODE_SHIFT, - GPLL_MODE_NORM << GPLL_MODE_SHIFT | - APLL_MODE_NORM << APLL_MODE_SHIFT); -} - -/* Get pll rate by id */ -static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru, - enum rk_clk_id clk_id) -{ - uint32_t refdiv, fbdiv, postdiv1, postdiv2; - uint32_t con; - int pll_id = rk_pll_id(clk_id); - struct rk3036_pll *pll = &cru->pll[pll_id]; - static u8 clk_shift[CLK_COUNT] = { - 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff, - GPLL_MODE_SHIFT, 0xff - }; - static u8 clk_mask[CLK_COUNT] = { - 0xff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xff, - GPLL_MODE_MASK, 0xff - }; - uint shift; - uint mask; - - con = readl(&cru->cru_mode_con); - shift = clk_shift[clk_id]; - mask = clk_mask[clk_id]; - - switch ((con >> shift) & mask) { - case GPLL_MODE_SLOW: - return OSC_HZ; - case GPLL_MODE_NORM: - - /* normal mode */ - con = readl(&pll->con0); - postdiv1 = (con >> PLL_POSTDIV1_SHIFT) & PLL_POSTDIV1_MASK; - fbdiv = (con >> PLL_FBDIV_SHIFT) & PLL_FBDIV_MASK; - con = readl(&pll->con1); - postdiv2 = (con >> PLL_POSTDIV2_SHIFT) & PLL_POSTDIV2_MASK; - refdiv = (con >> PLL_REFDIV_SHIFT) & PLL_REFDIV_MASK; - return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; - case GPLL_MODE_DEEP: - default: - return 32768; - } -} - -static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate, - int periph) -{ - uint src_rate; - uint div, mux; - u32 con; - - switch (periph) { - case HCLK_EMMC: - con = readl(&cru->cru_clksel_con[12]); - mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; - div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; - break; - case HCLK_SDIO: - con = readl(&cru->cru_clksel_con[12]); - mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK; - div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; - break; - default: - return -EINVAL; - } - - src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate; - return DIV_TO_RATE(src_rate, div); -} - -static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate, - int periph, uint freq) -{ - int src_clk_div; - int mux; - - debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); - - /* mmc clock auto divide 2 in internal */ - src_clk_div = (clk_general_rate / 2 + freq - 1) / freq; - - if (src_clk_div > 0x7f) { - src_clk_div = (OSC_HZ / 2 + freq - 1) / freq; - mux = EMMC_SEL_24M; - } else { - mux = EMMC_SEL_GPLL; - } - - switch (periph) { - case HCLK_EMMC: - rk_clrsetreg(&cru->cru_clksel_con[12], - EMMC_PLL_MASK << EMMC_PLL_SHIFT | - EMMC_DIV_MASK << EMMC_DIV_SHIFT, - mux << EMMC_PLL_SHIFT | - (src_clk_div - 1) << EMMC_DIV_SHIFT); - break; - case HCLK_SDIO: - rk_clrsetreg(&cru->cru_clksel_con[11], - MMC0_PLL_MASK << MMC0_PLL_SHIFT | - MMC0_DIV_MASK << MMC0_DIV_SHIFT, - mux << MMC0_PLL_SHIFT | - (src_clk_div - 1) << MMC0_DIV_SHIFT); - break; - default: - return -EINVAL; - } - - return rockchip_mmc_get_clk(cru, clk_general_rate, periph); -} - -static ulong rk3036_clk_get_rate(struct clk *clk) -{ - struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); - - switch (clk->id) { - case 0 ... 63: - return rkclk_pll_get_rate(priv->cru, clk->id); - default: - return -ENOENT; - } -} - -static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate) -{ - struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); - ulong new_rate, gclk_rate; - - gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); - switch (clk->id) { - case 0 ... 63: - return 0; - case HCLK_EMMC: - new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate, - clk->id, rate); - break; - default: - return -ENOENT; - } - - return new_rate; -} - -static struct clk_ops rk3036_clk_ops = { - .get_rate = rk3036_clk_get_rate, - .set_rate = rk3036_clk_set_rate, -}; - -static int rk3036_clk_probe(struct udevice *dev) -{ - struct rk3036_clk_priv *priv = dev_get_priv(dev); - - priv->cru = (struct rk3036_cru *)dev_get_addr(dev); - rkclk_init(priv->cru); - - return 0; -} - -static int rk3036_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, "rk3036_sysreset", "reset", &dev); - if (ret) - debug("Warning: No RK3036 reset driver: ret=%d\n", ret); - - return 0; -} - -static const struct udevice_id rk3036_clk_ids[] = { - { .compatible = "rockchip,rk3036-cru" }, - { } -}; - -U_BOOT_DRIVER(clk_rk3036) = { - .name = "clk_rk3036", - .id = UCLASS_CLK, - .of_match = rk3036_clk_ids, - .priv_auto_alloc_size = sizeof(struct rk3036_clk_priv), - .ops = &rk3036_clk_ops, - .bind = rk3036_clk_bind, - .probe = rk3036_clk_probe, -}; diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c deleted file mode 100644 index e00feb0..0000000 --- a/drivers/clk/clk_rk3288.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * (C) Copyright 2015 Google, Inc - * - * SPDX-License-Identifier: GPL-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -struct rk3288_clk_plat { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct dtd_rockchip_rk3288_cru dtd; -#endif -}; - -struct rk3288_clk_priv { - struct rk3288_grf *grf; - struct rk3288_cru *cru; - ulong rate; -}; - -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 = 27500000, - FREF_MAX_HZ = 2200U * 1000000, - FREF_MIN_HZ = 269 * 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, - - /* CLKSEL0 */ - CORE_SEL_PLL_MASK = 1, - CORE_SEL_PLL_SHIFT = 15, - A17_DIV_MASK = 0x1f, - A17_DIV_SHIFT = 8, - MP_DIV_MASK = 0xf, - MP_DIV_SHIFT = 4, - M0_DIV_MASK = 0xf, - M0_DIV_SHIFT = 0, - - /* CLKSEL1: pd bus clk pll sel: codec or general */ - PD_BUS_SEL_PLL_MASK = 15, - PD_BUS_SEL_CPLL = 0, - PD_BUS_SEL_GPLL, - - /* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */ - PD_BUS_PCLK_DIV_SHIFT = 12, - PD_BUS_PCLK_DIV_MASK = 7, - - /* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ - PD_BUS_HCLK_DIV_SHIFT = 8, - PD_BUS_HCLK_DIV_MASK = 3, - - /* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */ - PD_BUS_ACLK_DIV0_SHIFT = 3, - PD_BUS_ACLK_DIV0_MASK = 0x1f, - PD_BUS_ACLK_DIV1_SHIFT = 0, - PD_BUS_ACLK_DIV1_MASK = 0x7, - - /* - * CLKSEL10 - * peripheral bus pclk div: - * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1 - */ - PERI_SEL_PLL_MASK = 1, - PERI_SEL_PLL_SHIFT = 15, - PERI_SEL_CPLL = 0, - PERI_SEL_GPLL, - - PERI_PCLK_DIV_SHIFT = 12, - PERI_PCLK_DIV_MASK = 3, - - /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ - PERI_HCLK_DIV_SHIFT = 8, - PERI_HCLK_DIV_MASK = 3, - - /* - * peripheral bus aclk div: - * aclk_periph = periph_clk_src / (peri_aclk_div_con + 1) - */ - PERI_ACLK_DIV_SHIFT = 0, - PERI_ACLK_DIV_MASK = 0x1f, - - SOCSTS_DPLL_LOCK = 1 << 5, - SOCSTS_APLL_LOCK = 1 << 6, - SOCSTS_CPLL_LOCK = 1 << 7, - SOCSTS_GPLL_LOCK = 1 << 8, - SOCSTS_NPLL_LOCK = 1 << 9, -}; - -#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 rk3288_clk_priv *priv; - struct udevice *dev; - int ret; - - ret = rockchip_get_clk(&dev); - if (ret) - return ERR_PTR(ret); - - priv = dev_get_priv(dev); - - return priv->cru; -} - -static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, - const struct pll_div *div) -{ - int pll_id = rk_pll_id(clk_id); - struct rk3288_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); - 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) -{ - return fls(value) - 1; -} - -static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, - unsigned int hz) -{ - static const struct pll_div dpll_cfg[] = { - {.nf = 25, .nr = 2, .no = 1}, - {.nf = 400, .nr = 9, .no = 2}, - {.nf = 500, .nr = 9, .no = 2}, - {.nf = 100, .nr = 3, .no = 1}, - }; - int cfg; - - switch (hz) { - case 300000000: - cfg = 0; - break; - case 533000000: /* actually 533.3P MHz */ - cfg = 1; - break; - case 666000000: /* actually 666.6P MHz */ - cfg = 2; - break; - case 800000000: - cfg = 3; - break; - default: - debug("Unsupported SDRAM frequency"); - return -EINVAL; - } - - /* pll enter slow-mode */ - rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, - DPLL_MODE_SLOW << DPLL_MODE_SHIFT); - - rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]); - - /* wait for pll lock */ - while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK)) - udelay(1); - - /* PLL enter normal-mode */ - rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, - DPLL_MODE_NORMAL << DPLL_MODE_SHIFT); - - return 0; -} - -#ifndef CONFIG_SPL_BUILD -#define VCO_MAX_KHZ 2200000 -#define VCO_MIN_KHZ 440000 -#define FREF_MAX_KHZ 2200000 -#define FREF_MIN_KHZ 269 - -static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div) -{ - uint ref_khz = OSC_HZ / 1000, nr, nf = 0; - uint fref_khz; - uint diff_khz, best_diff_khz; - const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4; - uint vco_khz; - uint no = 1; - uint freq_khz = freq_hz / 1000; - - if (!freq_hz) { - printf("%s: the frequency can not be 0 Hz\n", __func__); - return -EINVAL; - } - - no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz); - if (ext_div) { - *ext_div = DIV_ROUND_UP(no, max_no); - no = DIV_ROUND_UP(no, *ext_div); - } - - /* only even divisors (and 1) are supported */ - if (no > 1) - no = DIV_ROUND_UP(no, 2) * 2; - - vco_khz = freq_khz * no; - if (ext_div) - vco_khz *= *ext_div; - - if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) { - printf("%s: Cannot find out a supported VCO for Frequency (%luHz).\n", - __func__, freq_hz); - return -1; - } - - div->no = no; - - best_diff_khz = vco_khz; - for (nr = 1; nr < max_nr && best_diff_khz; nr++) { - fref_khz = ref_khz / nr; - if (fref_khz < FREF_MIN_KHZ) - break; - if (fref_khz > FREF_MAX_KHZ) - continue; - - nf = vco_khz / fref_khz; - if (nf >= max_nf) - continue; - diff_khz = vco_khz - nf * fref_khz; - if (nf + 1 < max_nf && diff_khz > fref_khz / 2) { - nf++; - diff_khz = fref_khz - diff_khz; - } - - if (diff_khz >= best_diff_khz) - continue; - - best_diff_khz = diff_khz; - div->nr = nr; - div->nf = nf; - } - - if (best_diff_khz > 4 * 1000) { - printf("%s: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n", - __func__, freq_hz, best_diff_khz * 1000); - return -EINVAL; - } - - return 0; -} - -static int rockchip_mac_set_clk(struct rk3288_cru *cru, - int periph, uint freq) -{ - /* Assuming mac_clk is fed by an external clock */ - rk_clrsetreg(&cru->cru_clksel_con[21], - RMII_EXTCLK_MASK << RMII_EXTCLK_SHIFT, - RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT); - - return 0; -} - -static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf, - int periph, unsigned int rate_hz) -{ - struct pll_div npll_config = {0}; - u32 lcdc_div; - int ret; - - ret = pll_para_config(rate_hz, &npll_config, &lcdc_div); - if (ret) - return ret; - - rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, - NPLL_MODE_SLOW << NPLL_MODE_SHIFT); - rkclk_set_pll(cru, CLK_NEW, &npll_config); - - /* waiting for pll lock */ - while (1) { - if (readl(&grf->soc_status[1]) & SOCSTS_NPLL_LOCK) - break; - udelay(1); - } - - rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, - NPLL_MODE_NORMAL << NPLL_MODE_SHIFT); - - /* vop dclk source clk: npll,dclk_div: 1 */ - switch (periph) { - case DCLK_VOP0: - rk_clrsetreg(&cru->cru_clksel_con[27], 0xff << 8 | 3 << 0, - (lcdc_div - 1) << 8 | 2 << 0); - break; - case DCLK_VOP1: - rk_clrsetreg(&cru->cru_clksel_con[29], 0xff << 8 | 3 << 6, - (lcdc_div - 1) << 8 | 2 << 6); - break; - } - - return 0; -} -#endif - -#ifdef CONFIG_SPL_BUILD -static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) -{ - u32 aclk_div; - u32 hclk_div; - u32 pclk_div; - - /* pll enter slow-mode */ - rk_clrsetreg(&cru->cru_mode_con, - GPLL_MODE_MASK << GPLL_MODE_SHIFT | - CPLL_MODE_MASK << CPLL_MODE_SHIFT, - GPLL_MODE_SLOW << GPLL_MODE_SHIFT | - CPLL_MODE_SLOW << CPLL_MODE_SHIFT); - - /* init pll */ - rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); - rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); - - /* waiting for pll lock */ - while ((readl(&grf->soc_status[1]) & - (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) != - (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) - udelay(1); - - /* - * pd_bus clock pll source selection and - * set up dependent divisors for PCLK/HCLK and ACLK clocks. - */ - aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1; - assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); - hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1; - assert((hclk_div + 1) * PD_BUS_HCLK_HZ == - PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2)); - - pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1; - assert((pclk_div + 1) * PD_BUS_PCLK_HZ == - PD_BUS_ACLK_HZ && pclk_div < 0x7); - - rk_clrsetreg(&cru->cru_clksel_con[1], - PD_BUS_PCLK_DIV_MASK << PD_BUS_PCLK_DIV_SHIFT | - PD_BUS_HCLK_DIV_MASK << PD_BUS_HCLK_DIV_SHIFT | - PD_BUS_ACLK_DIV0_MASK << PD_BUS_ACLK_DIV0_SHIFT | - PD_BUS_ACLK_DIV1_MASK << PD_BUS_ACLK_DIV1_SHIFT, - pclk_div << PD_BUS_PCLK_DIV_SHIFT | - hclk_div << PD_BUS_HCLK_DIV_SHIFT | - aclk_div << PD_BUS_ACLK_DIV0_SHIFT | - 0 << 0); - - /* - * peri clock pll source selection and - * set up dependent divisors for PCLK/HCLK and ACLK clocks. - */ - aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; - assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); - - hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); - assert((1 << hclk_div) * PERI_HCLK_HZ == - PERI_ACLK_HZ && (hclk_div < 0x4)); - - pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); - assert((1 << pclk_div) * PERI_PCLK_HZ == - PERI_ACLK_HZ && (pclk_div < 0x4)); - - rk_clrsetreg(&cru->cru_clksel_con[10], - PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | - PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | - PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, - PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | - pclk_div << PERI_PCLK_DIV_SHIFT | - hclk_div << PERI_HCLK_DIV_SHIFT | - aclk_div << PERI_ACLK_DIV_SHIFT); - - /* PLL enter normal-mode */ - rk_clrsetreg(&cru->cru_mode_con, - GPLL_MODE_MASK << GPLL_MODE_SHIFT | - CPLL_MODE_MASK << CPLL_MODE_SHIFT, - GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | - CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); -} -#endif - -void rk3288_clk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf) -{ - /* pll enter slow-mode */ - rk_clrsetreg(&cru->cru_mode_con, - APLL_MODE_MASK << APLL_MODE_SHIFT, - APLL_MODE_SLOW << APLL_MODE_SHIFT); - - rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); - - /* waiting for pll lock */ - while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK)) - udelay(1); - - /* - * core clock pll source selection and - * set up dependent divisors for MPAXI/M0AXI and ARM clocks. - * core clock select apll, apll clk = 1800MHz - * arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz - */ - rk_clrsetreg(&cru->cru_clksel_con[0], - CORE_SEL_PLL_MASK << CORE_SEL_PLL_SHIFT | - A17_DIV_MASK << A17_DIV_SHIFT | - MP_DIV_MASK << MP_DIV_SHIFT | - M0_DIV_MASK << M0_DIV_SHIFT, - 0 << A17_DIV_SHIFT | - 3 << MP_DIV_SHIFT | - 1 << M0_DIV_SHIFT); - - /* - * set up dependent divisors for L2RAM/ATCLK and PCLK clocks. - * l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz - */ - rk_clrsetreg(&cru->cru_clksel_con[37], - CLK_L2RAM_DIV_MASK << CLK_L2RAM_DIV_SHIFT | - ATCLK_CORE_DIV_CON_MASK << ATCLK_CORE_DIV_CON_SHIFT | - PCLK_CORE_DBG_DIV_MASK >> PCLK_CORE_DBG_DIV_SHIFT, - 1 << CLK_L2RAM_DIV_SHIFT | - 3 << ATCLK_CORE_DIV_CON_SHIFT | - 3 << PCLK_CORE_DBG_DIV_SHIFT); - - /* PLL enter normal-mode */ - rk_clrsetreg(&cru->cru_mode_con, - APLL_MODE_MASK << APLL_MODE_SHIFT, - APLL_MODE_NORMAL << APLL_MODE_SHIFT); -} - -/* Get pll rate by id */ -static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, - enum rk_clk_id clk_id) -{ - uint32_t nr, no, nf; - uint32_t con; - int pll_id = rk_pll_id(clk_id); - struct rk3288_pll *pll = &cru->pll[pll_id]; - static u8 clk_shift[CLK_COUNT] = { - 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, - GPLL_MODE_SHIFT, NPLL_MODE_SHIFT - }; - uint shift; - - con = readl(&cru->cru_mode_con); - shift = clk_shift[clk_id]; - switch ((con >> shift) & APLL_MODE_MASK) { - case APLL_MODE_SLOW: - return OSC_HZ; - case APLL_MODE_NORMAL: - /* normal mode */ - con = readl(&pll->con0); - no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; - nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1; - con = readl(&pll->con1); - nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; - - return (24 * nf / (nr * no)) * 1000000; - case APLL_MODE_DEEP: - default: - return 32768; - } -} - -static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate, - int periph) -{ - uint src_rate; - uint div, mux; - u32 con; - - switch (periph) { - case HCLK_EMMC: - con = readl(&cru->cru_clksel_con[12]); - mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; - div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; - break; - case HCLK_SDMMC: - con = readl(&cru->cru_clksel_con[11]); - mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK; - div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; - break; - case HCLK_SDIO0: - con = readl(&cru->cru_clksel_con[12]); - mux = (con >> SDIO0_PLL_SHIFT) & SDIO0_PLL_MASK; - div = (con >> SDIO0_DIV_SHIFT) & SDIO0_DIV_MASK; - break; - default: - return -EINVAL; - } - - src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate; - return DIV_TO_RATE(src_rate, div); -} - -static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, - int periph, uint freq) -{ - int src_clk_div; - int mux; - - debug("%s: gclk_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); - - if (src_clk_div > 0x3f) { - src_clk_div = RATE_TO_DIV(OSC_HZ, freq); - mux = EMMC_PLL_SELECT_24MHZ; - assert((int)EMMC_PLL_SELECT_24MHZ == - (int)MMC0_PLL_SELECT_24MHZ); - } else { - mux = EMMC_PLL_SELECT_GENERAL; - assert((int)EMMC_PLL_SELECT_GENERAL == - (int)MMC0_PLL_SELECT_GENERAL); - } - switch (periph) { - case HCLK_EMMC: - rk_clrsetreg(&cru->cru_clksel_con[12], - EMMC_PLL_MASK << EMMC_PLL_SHIFT | - EMMC_DIV_MASK << EMMC_DIV_SHIFT, - mux << EMMC_PLL_SHIFT | - (src_clk_div - 1) << EMMC_DIV_SHIFT); - break; - case HCLK_SDMMC: - rk_clrsetreg(&cru->cru_clksel_con[11], - MMC0_PLL_MASK << MMC0_PLL_SHIFT | - MMC0_DIV_MASK << MMC0_DIV_SHIFT, - mux << MMC0_PLL_SHIFT | - (src_clk_div - 1) << MMC0_DIV_SHIFT); - break; - case HCLK_SDIO0: - rk_clrsetreg(&cru->cru_clksel_con[12], - SDIO0_PLL_MASK << SDIO0_PLL_SHIFT | - SDIO0_DIV_MASK << SDIO0_DIV_SHIFT, - mux << SDIO0_PLL_SHIFT | - (src_clk_div - 1) << SDIO0_DIV_SHIFT); - break; - default: - return -EINVAL; - } - - return rockchip_mmc_get_clk(cru, gclk_rate, periph); -} - -static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint gclk_rate, - int periph) -{ - uint div, mux; - u32 con; - - switch (periph) { - case SCLK_SPI0: - con = readl(&cru->cru_clksel_con[25]); - mux = (con >> SPI0_PLL_SHIFT) & SPI0_PLL_MASK; - div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK; - break; - case SCLK_SPI1: - con = readl(&cru->cru_clksel_con[25]); - mux = (con >> SPI1_PLL_SHIFT) & SPI1_PLL_MASK; - div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK; - break; - case SCLK_SPI2: - con = readl(&cru->cru_clksel_con[39]); - mux = (con >> SPI2_PLL_SHIFT) & SPI2_PLL_MASK; - div = (con >> SPI2_DIV_SHIFT) & SPI2_DIV_MASK; - break; - default: - return -EINVAL; - } - assert(mux == SPI0_PLL_SELECT_GENERAL); - - return DIV_TO_RATE(gclk_rate, div); -} - -static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate, - int periph, uint freq) -{ - int src_clk_div; - - debug("%s: clk_general_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); - switch (periph) { - case SCLK_SPI0: - rk_clrsetreg(&cru->cru_clksel_con[25], - SPI0_PLL_MASK << SPI0_PLL_SHIFT | - SPI0_DIV_MASK << SPI0_DIV_SHIFT, - SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT | - src_clk_div << SPI0_DIV_SHIFT); - break; - case SCLK_SPI1: - rk_clrsetreg(&cru->cru_clksel_con[25], - SPI1_PLL_MASK << SPI1_PLL_SHIFT | - SPI1_DIV_MASK << SPI1_DIV_SHIFT, - SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT | - src_clk_div << SPI1_DIV_SHIFT); - break; - case SCLK_SPI2: - rk_clrsetreg(&cru->cru_clksel_con[39], - SPI2_PLL_MASK << SPI2_PLL_SHIFT | - SPI2_DIV_MASK << SPI2_DIV_SHIFT, - SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT | - src_clk_div << SPI2_DIV_SHIFT); - break; - default: - return -EINVAL; - } - - return rockchip_spi_get_clk(cru, gclk_rate, periph); -} - -static ulong rk3288_clk_get_rate(struct clk *clk) -{ - struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); - ulong new_rate, gclk_rate; - - gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); - switch (clk->id) { - case 0 ... 63: - new_rate = rkclk_pll_get_rate(priv->cru, clk->id); - break; - case HCLK_EMMC: - case HCLK_SDMMC: - case HCLK_SDIO0: - new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id); - break; - case SCLK_SPI0: - case SCLK_SPI1: - case SCLK_SPI2: - new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, clk->id); - break; - case PCLK_I2C0: - case PCLK_I2C1: - case PCLK_I2C2: - case PCLK_I2C3: - case PCLK_I2C4: - case PCLK_I2C5: - return gclk_rate; - default: - return -ENOENT; - } - - return new_rate; -} - -static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) -{ - struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); - struct rk3288_cru *cru = priv->cru; - ulong new_rate, gclk_rate; - - gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); - switch (clk->id) { - case CLK_DDR: - new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate); - break; - case HCLK_EMMC: - case HCLK_SDMMC: - case HCLK_SDIO0: - new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate); - break; - case SCLK_SPI0: - case SCLK_SPI1: - case SCLK_SPI2: - new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate); - break; -#ifndef CONFIG_SPL_BUILD - case SCLK_MAC: - new_rate = rockchip_mac_set_clk(priv->cru, clk->id, rate); - break; - case DCLK_VOP0: - case DCLK_VOP1: - new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate); - break; - case SCLK_EDP_24M: - /* clk_edp_24M source: 24M */ - rk_setreg(&cru->cru_clksel_con[28], 1 << 15); - - /* rst edp */ - rk_setreg(&cru->cru_clksel_con[6], 1 << 15); - udelay(1); - rk_clrreg(&cru->cru_clksel_con[6], 1 << 15); - new_rate = rate; - break; - case ACLK_VOP0: - case ACLK_VOP1: { - u32 div; - - /* vop aclk source clk: cpll */ - div = CPLL_HZ / rate; - assert((div - 1 < 64) && (div * rate == CPLL_HZ)); - - switch (clk->id) { - case ACLK_VOP0: - rk_clrsetreg(&cru->cru_clksel_con[31], - 3 << 6 | 0x1f << 0, - 0 << 6 | (div - 1) << 0); - break; - case ACLK_VOP1: - rk_clrsetreg(&cru->cru_clksel_con[31], - 3 << 14 | 0x1f << 8, - 0 << 14 | (div - 1) << 8); - break; - } - new_rate = rate; - break; - } - case PCLK_HDMI_CTRL: - /* enable pclk hdmi ctrl */ - rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9); - - /* software reset hdmi */ - rk_setreg(&cru->cru_clkgate_con[7], 1 << 9); - udelay(1); - rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9); - new_rate = rate; - break; -#endif - default: - return -ENOENT; - } - - return new_rate; -} - -static struct clk_ops rk3288_clk_ops = { - .get_rate = rk3288_clk_get_rate, - .set_rate = rk3288_clk_set_rate, -}; - -static int rk3288_clk_ofdata_to_platdata(struct udevice *dev) -{ -#if !CONFIG_IS_ENABLED(OF_PLATDATA) - struct rk3288_clk_priv *priv = dev_get_priv(dev); - - priv->cru = (struct rk3288_cru *)dev_get_addr(dev); -#endif - - return 0; -} - -static int rk3288_clk_probe(struct udevice *dev) -{ - struct rk3288_clk_priv *priv = dev_get_priv(dev); - - priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); - if (IS_ERR(priv->grf)) - return PTR_ERR(priv->grf); -#ifdef CONFIG_SPL_BUILD -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct rk3288_clk_plat *plat = dev_get_platdata(dev); - - priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); -#endif - rkclk_init(priv->cru, priv->grf); -#endif - - return 0; -} - -static int rk3288_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, "rk3288_sysreset", "reset", &dev); - if (ret) - debug("Warning: No RK3288 reset driver: ret=%d\n", ret); - - return 0; -} - -static const struct udevice_id rk3288_clk_ids[] = { - { .compatible = "rockchip,rk3288-cru" }, - { } -}; - -U_BOOT_DRIVER(rockchip_rk3288_cru) = { - .name = "rockchip_rk3288_cru", - .id = UCLASS_CLK, - .of_match = rk3288_clk_ids, - .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv), - .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat), - .ops = &rk3288_clk_ops, - .bind = rk3288_clk_bind, - .ofdata_to_platdata = rk3288_clk_ofdata_to_platdata, - .probe = rk3288_clk_probe, -}; diff --git a/drivers/clk/clk_rk3399.c b/drivers/clk/clk_rk3399.c deleted file mode 100644 index c0bb80c..0000000 --- a/drivers/clk/clk_rk3399.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * (C) Copyright 2015 Google, Inc - * - * SPDX-License-Identifier: GPL-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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_SHIFT = 12, - PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, - PLL_POSTDIV1_SHIFT = 8, - PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT, - PLL_REFDIV_MASK = 0x3f, - PLL_REFDIV_SHIFT = 0, - - /* PLL_CON2 */ - PLL_LOCK_STATUS_SHIFT = 31, - PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, - PLL_FRACDIV_MASK = 0xffffff, - PLL_FRACDIV_SHIFT = 0, - - /* PLL_CON3 */ - PLL_MODE_SHIFT = 8, - PLL_MODE_MASK = 3 << PLL_MODE_SHIFT, - PLL_MODE_SLOW = 0, - PLL_MODE_NORM, - PLL_MODE_DEEP, - PLL_DSMPD_SHIFT = 3, - PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, - 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_SHIFT = 7, - SPI3_PLL_SEL_MASK = 1 << SPI3_PLL_SEL_SHIFT, - SPI3_PLL_SEL_24M = 0, - SPI3_PLL_SEL_PPLL = 1, - SPI3_DIV_CON_SHIFT = 0x0, - SPI3_DIV_CON_MASK = 0x7f, - - /* 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_SHIFT = 8, - ACLKM_CORE_L_DIV_CON_MASK = 0x1f << ACLKM_CORE_L_DIV_CON_SHIFT, - CLK_CORE_L_PLL_SEL_SHIFT = 6, - CLK_CORE_L_PLL_SEL_MASK = 3 << CLK_CORE_L_PLL_SEL_SHIFT, - 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_SHIFT = 0x8, - PCLK_DBG_L_DIV_MASK = 0x1f << PCLK_DBG_L_DIV_SHIFT, - ATCLK_CORE_L_DIV_SHIFT = 0, - ATCLK_CORE_L_DIV_MASK = 0x1f << ATCLK_CORE_L_DIV_SHIFT, - - /* CLKSEL_CON14 */ - PCLK_PERIHP_DIV_CON_SHIFT = 12, - PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, - HCLK_PERIHP_DIV_CON_SHIFT = 8, - HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT, - ACLK_PERIHP_PLL_SEL_SHIFT = 7, - ACLK_PERIHP_PLL_SEL_MASK = 1 << ACLK_PERIHP_PLL_SEL_SHIFT, - ACLK_PERIHP_PLL_SEL_CPLL = 0, - ACLK_PERIHP_PLL_SEL_GPLL = 1, - ACLK_PERIHP_DIV_CON_SHIFT = 0, - ACLK_PERIHP_DIV_CON_MASK = 0x1f, - - /* CLKSEL_CON21 */ - ACLK_EMMC_PLL_SEL_SHIFT = 7, - ACLK_EMMC_PLL_SEL_MASK = 0x1 << ACLK_EMMC_PLL_SEL_SHIFT, - ACLK_EMMC_PLL_SEL_GPLL = 0x1, - ACLK_EMMC_DIV_CON_SHIFT = 0, - ACLK_EMMC_DIV_CON_MASK = 0x1f, - - /* CLKSEL_CON22 */ - CLK_EMMC_PLL_SHIFT = 8, - CLK_EMMC_PLL_MASK = 0x7 << CLK_EMMC_PLL_SHIFT, - CLK_EMMC_PLL_SEL_GPLL = 0x1, - CLK_EMMC_DIV_CON_SHIFT = 0, - CLK_EMMC_DIV_CON_MASK = 0x7f << CLK_EMMC_DIV_CON_SHIFT, - - /* CLKSEL_CON23 */ - PCLK_PERILP0_DIV_CON_SHIFT = 12, - PCLK_PERILP0_DIV_CON_MASK = 0x7 << PCLK_PERILP0_DIV_CON_SHIFT, - HCLK_PERILP0_DIV_CON_SHIFT = 8, - HCLK_PERILP0_DIV_CON_MASK = 3 << HCLK_PERILP0_DIV_CON_SHIFT, - ACLK_PERILP0_PLL_SEL_SHIFT = 7, - ACLK_PERILP0_PLL_SEL_MASK = 1 << ACLK_PERILP0_PLL_SEL_SHIFT, - ACLK_PERILP0_PLL_SEL_CPLL = 0, - ACLK_PERILP0_PLL_SEL_GPLL = 1, - ACLK_PERILP0_DIV_CON_SHIFT = 0, - ACLK_PERILP0_DIV_CON_MASK = 0x1f, - - /* CLKSEL_CON25 */ - PCLK_PERILP1_DIV_CON_SHIFT = 8, - PCLK_PERILP1_DIV_CON_MASK = 0x7 << PCLK_PERILP1_DIV_CON_SHIFT, - HCLK_PERILP1_PLL_SEL_SHIFT = 7, - HCLK_PERILP1_PLL_SEL_MASK = 1 << HCLK_PERILP1_PLL_SEL_SHIFT, - HCLK_PERILP1_PLL_SEL_CPLL = 0, - HCLK_PERILP1_PLL_SEL_GPLL = 1, - HCLK_PERILP1_DIV_CON_SHIFT = 0, - HCLK_PERILP1_DIV_CON_MASK = 0x1f, - - /* CLKSEL_CON26 */ - CLK_SARADC_DIV_CON_SHIFT = 8, - CLK_SARADC_DIV_CON_MASK = 0xff << CLK_SARADC_DIV_CON_SHIFT, - - /* CLKSEL_CON27 */ - CLK_TSADC_SEL_X24M = 0x0, - CLK_TSADC_SEL_SHIFT = 15, - CLK_TSADC_SEL_MASK = 1 << CLK_TSADC_SEL_SHIFT, - CLK_TSADC_DIV_CON_SHIFT = 0, - CLK_TSADC_DIV_CON_MASK = 0x3ff, - - /* CLKSEL_CON47 & CLKSEL_CON48 */ - ACLK_VOP_PLL_SEL_SHIFT = 6, - ACLK_VOP_PLL_SEL_MASK = 0x3 << ACLK_VOP_PLL_SEL_SHIFT, - ACLK_VOP_PLL_SEL_CPLL = 0x1, - ACLK_VOP_DIV_CON_SHIFT = 0, - ACLK_VOP_DIV_CON_MASK = 0x1f << ACLK_VOP_DIV_CON_SHIFT, - - /* CLKSEL_CON49 & CLKSEL_CON50 */ - DCLK_VOP_DCLK_SEL_SHIFT = 11, - DCLK_VOP_DCLK_SEL_MASK = 1 << DCLK_VOP_DCLK_SEL_SHIFT, - DCLK_VOP_DCLK_SEL_DIVOUT = 0, - DCLK_VOP_PLL_SEL_SHIFT = 8, - DCLK_VOP_PLL_SEL_MASK = 3 << DCLK_VOP_PLL_SEL_SHIFT, - 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_SHIFT = 8, - RESETN_DDR0_REQ_MASK = 1 << RESETN_DDR0_REQ_SHIFT, - RESETN_DDRPHY0_REQ_SHIFT = 9, - RESETN_DDRPHY0_REQ_MASK = 1 << RESETN_DDRPHY0_REQ_SHIFT, - RESETN_DDR1_REQ_SHIFT = 12, - RESETN_DDR1_REQ_MASK = 1 << RESETN_DDR1_REQ_SHIFT, - RESETN_DDRPHY1_REQ_SHIFT = 13, - RESETN_DDRPHY1_REQ_MASK = 1 << RESETN_DDRPHY1_REQ_SHIFT, -}; - -#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 restructions of pll in integer mode, 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; - - 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_SLOW << PLL_MODE_SHIFT); - - /* use integer mode */ - rk_clrsetreg(&pll_con[3], PLL_DSMPD_MASK, - PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); - - rk_clrsetreg(&pll_con[0], PLL_FBDIV_MASK, - div->fbdiv << PLL_FBDIV_SHIFT); - rk_clrsetreg(&pll_con[1], - PLL_POSTDIV2_MASK | PLL_POSTDIV1_MASK | - 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_NORM << PLL_MODE_SHIFT); -} - -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 three lines as a fix of bootrom bug. - */ - 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 | HCLK_PERIHP_DIV_CON_MASK | - ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK, - 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 | HCLK_PERILP0_DIV_CON_MASK | - ACLK_PERILP0_PLL_SEL_MASK | ACLK_PERILP0_DIV_CON_MASK, - 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 | HCLK_PERILP1_DIV_CON_MASK | - HCLK_PERILP1_PLL_SEL_MASK, - 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 | CLK_CORE_L_PLL_SEL_MASK | - CLK_CORE_L_DIV_MASK, - 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 | ATCLK_CORE_L_DIV_MASK, - 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_DIV_CON_MASK, - 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_PLL_SEL_MASK| - DCLK_VOP_DIV_CON_MASK, - 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_DIV_CON_MASK, - 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_DIV_CON_MASK, - 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_DIV_CON_MASK, - 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); - ulong ret = 0; - - switch (clk->id) { - case 0 ... 63: - return 0; - case SCLK_SDMMC: - case SCLK_EMMC: - ret = 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: - ret = rk3399_i2c_set_clk(priv->cru, clk->id, rate); - break; - case DCLK_VOP0: - case DCLK_VOP1: - rate = rk3399_vop_set_clk(priv->cru, clk->id, rate); - break; - default: - return -ENOENT; - } - - return ret; -} - -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, "clk_rk3399", &dev); - if (ret) - return ERR_PTR(ret); - - addr = dev_get_addr_ptr(dev); - if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) - return ERR_PTR(-EINVAL); - - return addr; -} - -static int rk3399_clk_probe(struct udevice *dev) -{ - struct rk3399_clk_priv *priv = dev_get_priv(dev); - - rkclk_init(priv->cru); - - return 0; -} - -static int rk3399_clk_ofdata_to_platdata(struct udevice *dev) -{ - struct rk3399_clk_priv *priv = dev_get_priv(dev); - - priv->cru = (struct rk3399_cru *)dev_get_addr(dev); - - 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), - .ofdata_to_platdata = rk3399_clk_ofdata_to_platdata, - .ops = &rk3399_clk_ops, - .bind = rk3399_clk_bind, - .probe = rk3399_clk_probe, -}; diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile new file mode 100644 index 0000000..1f8e417 --- /dev/null +++ b/drivers/clk/rockchip/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2016 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o +obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o +obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c new file mode 100644 index 0000000..6202c9d --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3036.c @@ -0,0 +1,386 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rk3036_clk_priv { + struct rk3036_cru *cru; + ulong rate; +}; + +enum { + VCO_MAX_HZ = 2400U * 1000000, + VCO_MIN_HZ = 600 * 1000000, + OUTPUT_MAX_HZ = 2400U * 1000000, + OUTPUT_MIN_HZ = 24 * 1000000, +}; + +#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_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\ + OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\ + #hz "Hz cannot be hit with PLL "\ + "divisors on line " __stringify(__LINE__)); + +/* use interge mode*/ +static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); +static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); + +static inline unsigned int log2(unsigned int value) +{ + return fls(value) - 1; +} + +void *rockchip_get_cru(void) +{ + struct udevice *dev; + fdt_addr_t addr; + int ret; + + ret = uclass_get_device(UCLASS_CLK, 0, &dev); + if (ret) + return ERR_PTR(ret); + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return ERR_PTR(-EINVAL); + + return (void *)addr; +} + +static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id, + const struct pll_div *div) +{ + int pll_id = rk_pll_id(clk_id); + struct rk3036_pll *pll = &cru->pll[pll_id]; + + /* All PLLs have same VCO and output frequency range restrictions. */ + uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; + uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; + + debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\ + vco=%u Hz, output=%u Hz\n", + pll, div->fbdiv, div->refdiv, div->postdiv1, + div->postdiv2, 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); + + /* use interger mode */ + rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); + + rk_clrsetreg(&pll->con0, + PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK, + (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv); + rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT | + PLL_REFDIV_MASK << PLL_REFDIV_SHIFT, + (div->postdiv2 << PLL_POSTDIV2_SHIFT | + div->refdiv << PLL_REFDIV_SHIFT)); + + /* waiting for pll lock */ + while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)) + udelay(1); + + return 0; +} + +static void rkclk_init(struct rk3036_cru *cru) +{ + u32 aclk_div; + u32 hclk_div; + u32 pclk_div; + + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + APLL_MODE_MASK << APLL_MODE_SHIFT, + GPLL_MODE_SLOW << GPLL_MODE_SHIFT | + APLL_MODE_SLOW << APLL_MODE_SHIFT); + + /* init pll */ + rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); + rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); + + /* + * select apll as core clock pll source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + * core hz : apll = 1:1 + */ + aclk_div = APLL_HZ / CORE_ACLK_HZ - 1; + assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7); + + pclk_div = APLL_HZ / CORE_PERI_HZ - 1; + assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf); + + rk_clrsetreg(&cru->cru_clksel_con[0], + CORE_CLK_PLL_SEL_MASK << CORE_CLK_PLL_SEL_SHIFT | + CORE_DIV_CON_MASK << CORE_DIV_CON_SHIFT, + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + + rk_clrsetreg(&cru->cru_clksel_con[1], + CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT | + CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT, + aclk_div << CORE_ACLK_DIV_SHIFT | + pclk_div << CORE_PERI_DIV_SHIFT); + + /* + * select apll as cpu clock pll source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = APLL_HZ / CPU_ACLK_HZ - 1; + assert((aclk_div + 1) * CPU_ACLK_HZ == APLL_HZ && aclk_div < 0x1f); + + pclk_div = APLL_HZ / CPU_PCLK_HZ - 1; + assert((pclk_div + 1) * CPU_PCLK_HZ == APLL_HZ && pclk_div < 0x7); + + hclk_div = APLL_HZ / CPU_HCLK_HZ - 1; + assert((hclk_div + 1) * CPU_HCLK_HZ == APLL_HZ && hclk_div < 0x3); + + rk_clrsetreg(&cru->cru_clksel_con[0], + CPU_CLK_PLL_SEL_MASK << CPU_CLK_PLL_SEL_SHIFT | + ACLK_CPU_DIV_MASK << ACLK_CPU_DIV_SHIFT, + CPU_CLK_PLL_SEL_APLL << CPU_CLK_PLL_SEL_SHIFT | + aclk_div << ACLK_CPU_DIV_SHIFT); + + rk_clrsetreg(&cru->cru_clksel_con[1], + CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT | + CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT, + pclk_div << CPU_PCLK_DIV_SHIFT | + hclk_div << CPU_HCLK_DIV_SHIFT); + + /* + * select gpll as peri clock pll source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; + assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + + hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); + assert((1 << hclk_div) * PERI_HCLK_HZ == + PERI_ACLK_HZ && (pclk_div < 0x4)); + + pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); + assert((1 << pclk_div) * PERI_PCLK_HZ == + PERI_ACLK_HZ && pclk_div < 0x8); + + rk_clrsetreg(&cru->cru_clksel_con[10], + PERI_PLL_SEL_MASK << PERI_PLL_SEL_SHIFT | + PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | + PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | + PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, + PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | + pclk_div << PERI_PCLK_DIV_SHIFT | + hclk_div << PERI_HCLK_DIV_SHIFT | + aclk_div << PERI_ACLK_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + APLL_MODE_MASK << APLL_MODE_SHIFT, + GPLL_MODE_NORM << GPLL_MODE_SHIFT | + APLL_MODE_NORM << APLL_MODE_SHIFT); +} + +/* Get pll rate by id */ +static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru, + enum rk_clk_id clk_id) +{ + uint32_t refdiv, fbdiv, postdiv1, postdiv2; + uint32_t con; + int pll_id = rk_pll_id(clk_id); + struct rk3036_pll *pll = &cru->pll[pll_id]; + static u8 clk_shift[CLK_COUNT] = { + 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff, + GPLL_MODE_SHIFT, 0xff + }; + static u8 clk_mask[CLK_COUNT] = { + 0xff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xff, + GPLL_MODE_MASK, 0xff + }; + uint shift; + uint mask; + + con = readl(&cru->cru_mode_con); + shift = clk_shift[clk_id]; + mask = clk_mask[clk_id]; + + switch ((con >> shift) & mask) { + case GPLL_MODE_SLOW: + return OSC_HZ; + case GPLL_MODE_NORM: + + /* normal mode */ + con = readl(&pll->con0); + postdiv1 = (con >> PLL_POSTDIV1_SHIFT) & PLL_POSTDIV1_MASK; + fbdiv = (con >> PLL_FBDIV_SHIFT) & PLL_FBDIV_MASK; + con = readl(&pll->con1); + postdiv2 = (con >> PLL_POSTDIV2_SHIFT) & PLL_POSTDIV2_MASK; + refdiv = (con >> PLL_REFDIV_SHIFT) & PLL_REFDIV_MASK; + return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; + case GPLL_MODE_DEEP: + default: + return 32768; + } +} + +static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate, + int periph) +{ + uint src_rate; + uint div, mux; + u32 con; + + switch (periph) { + case HCLK_EMMC: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; + div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; + break; + case HCLK_SDIO: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK; + div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; + break; + default: + return -EINVAL; + } + + src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate; + return DIV_TO_RATE(src_rate, div); +} + +static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate, + int periph, uint freq) +{ + int src_clk_div; + int mux; + + debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); + + /* mmc clock auto divide 2 in internal */ + src_clk_div = (clk_general_rate / 2 + freq - 1) / freq; + + if (src_clk_div > 0x7f) { + src_clk_div = (OSC_HZ / 2 + freq - 1) / freq; + mux = EMMC_SEL_24M; + } else { + mux = EMMC_SEL_GPLL; + } + + switch (periph) { + case HCLK_EMMC: + rk_clrsetreg(&cru->cru_clksel_con[12], + EMMC_PLL_MASK << EMMC_PLL_SHIFT | + EMMC_DIV_MASK << EMMC_DIV_SHIFT, + mux << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + break; + case HCLK_SDIO: + rk_clrsetreg(&cru->cru_clksel_con[11], + MMC0_PLL_MASK << MMC0_PLL_SHIFT | + MMC0_DIV_MASK << MMC0_DIV_SHIFT, + mux << MMC0_PLL_SHIFT | + (src_clk_div - 1) << MMC0_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rockchip_mmc_get_clk(cru, clk_general_rate, periph); +} + +static ulong rk3036_clk_get_rate(struct clk *clk) +{ + struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); + + switch (clk->id) { + case 0 ... 63: + return rkclk_pll_get_rate(priv->cru, clk->id); + default: + return -ENOENT; + } +} + +static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); + ulong new_rate, gclk_rate; + + gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); + switch (clk->id) { + case 0 ... 63: + return 0; + case HCLK_EMMC: + new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate, + clk->id, rate); + break; + default: + return -ENOENT; + } + + return new_rate; +} + +static struct clk_ops rk3036_clk_ops = { + .get_rate = rk3036_clk_get_rate, + .set_rate = rk3036_clk_set_rate, +}; + +static int rk3036_clk_probe(struct udevice *dev) +{ + struct rk3036_clk_priv *priv = dev_get_priv(dev); + + priv->cru = (struct rk3036_cru *)dev_get_addr(dev); + rkclk_init(priv->cru); + + return 0; +} + +static int rk3036_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, "rk3036_sysreset", "reset", &dev); + if (ret) + debug("Warning: No RK3036 reset driver: ret=%d\n", ret); + + return 0; +} + +static const struct udevice_id rk3036_clk_ids[] = { + { .compatible = "rockchip,rk3036-cru" }, + { } +}; + +U_BOOT_DRIVER(clk_rk3036) = { + .name = "clk_rk3036", + .id = UCLASS_CLK, + .of_match = rk3036_clk_ids, + .priv_auto_alloc_size = sizeof(struct rk3036_clk_priv), + .ops = &rk3036_clk_ops, + .bind = rk3036_clk_bind, + .probe = rk3036_clk_probe, +}; diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c new file mode 100644 index 0000000..e00feb0 --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -0,0 +1,851 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rk3288_clk_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_cru dtd; +#endif +}; + +struct rk3288_clk_priv { + struct rk3288_grf *grf; + struct rk3288_cru *cru; + ulong rate; +}; + +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 = 27500000, + FREF_MAX_HZ = 2200U * 1000000, + FREF_MIN_HZ = 269 * 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, + + /* CLKSEL0 */ + CORE_SEL_PLL_MASK = 1, + CORE_SEL_PLL_SHIFT = 15, + A17_DIV_MASK = 0x1f, + A17_DIV_SHIFT = 8, + MP_DIV_MASK = 0xf, + MP_DIV_SHIFT = 4, + M0_DIV_MASK = 0xf, + M0_DIV_SHIFT = 0, + + /* CLKSEL1: pd bus clk pll sel: codec or general */ + PD_BUS_SEL_PLL_MASK = 15, + PD_BUS_SEL_CPLL = 0, + PD_BUS_SEL_GPLL, + + /* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */ + PD_BUS_PCLK_DIV_SHIFT = 12, + PD_BUS_PCLK_DIV_MASK = 7, + + /* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ + PD_BUS_HCLK_DIV_SHIFT = 8, + PD_BUS_HCLK_DIV_MASK = 3, + + /* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */ + PD_BUS_ACLK_DIV0_SHIFT = 3, + PD_BUS_ACLK_DIV0_MASK = 0x1f, + PD_BUS_ACLK_DIV1_SHIFT = 0, + PD_BUS_ACLK_DIV1_MASK = 0x7, + + /* + * CLKSEL10 + * peripheral bus pclk div: + * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1 + */ + PERI_SEL_PLL_MASK = 1, + PERI_SEL_PLL_SHIFT = 15, + PERI_SEL_CPLL = 0, + PERI_SEL_GPLL, + + PERI_PCLK_DIV_SHIFT = 12, + PERI_PCLK_DIV_MASK = 3, + + /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ + PERI_HCLK_DIV_SHIFT = 8, + PERI_HCLK_DIV_MASK = 3, + + /* + * peripheral bus aclk div: + * aclk_periph = periph_clk_src / (peri_aclk_div_con + 1) + */ + PERI_ACLK_DIV_SHIFT = 0, + PERI_ACLK_DIV_MASK = 0x1f, + + SOCSTS_DPLL_LOCK = 1 << 5, + SOCSTS_APLL_LOCK = 1 << 6, + SOCSTS_CPLL_LOCK = 1 << 7, + SOCSTS_GPLL_LOCK = 1 << 8, + SOCSTS_NPLL_LOCK = 1 << 9, +}; + +#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 rk3288_clk_priv *priv; + struct udevice *dev; + int ret; + + ret = rockchip_get_clk(&dev); + if (ret) + return ERR_PTR(ret); + + priv = dev_get_priv(dev); + + return priv->cru; +} + +static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, + const struct pll_div *div) +{ + int pll_id = rk_pll_id(clk_id); + struct rk3288_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); + 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) +{ + return fls(value) - 1; +} + +static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, + unsigned int hz) +{ + static const struct pll_div dpll_cfg[] = { + {.nf = 25, .nr = 2, .no = 1}, + {.nf = 400, .nr = 9, .no = 2}, + {.nf = 500, .nr = 9, .no = 2}, + {.nf = 100, .nr = 3, .no = 1}, + }; + int cfg; + + switch (hz) { + case 300000000: + cfg = 0; + break; + case 533000000: /* actually 533.3P MHz */ + cfg = 1; + break; + case 666000000: /* actually 666.6P MHz */ + cfg = 2; + break; + case 800000000: + cfg = 3; + break; + default: + debug("Unsupported SDRAM frequency"); + return -EINVAL; + } + + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_SLOW << DPLL_MODE_SHIFT); + + rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]); + + /* wait for pll lock */ + while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK)) + udelay(1); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_NORMAL << DPLL_MODE_SHIFT); + + return 0; +} + +#ifndef CONFIG_SPL_BUILD +#define VCO_MAX_KHZ 2200000 +#define VCO_MIN_KHZ 440000 +#define FREF_MAX_KHZ 2200000 +#define FREF_MIN_KHZ 269 + +static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div) +{ + uint ref_khz = OSC_HZ / 1000, nr, nf = 0; + uint fref_khz; + uint diff_khz, best_diff_khz; + const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4; + uint vco_khz; + uint no = 1; + uint freq_khz = freq_hz / 1000; + + if (!freq_hz) { + printf("%s: the frequency can not be 0 Hz\n", __func__); + return -EINVAL; + } + + no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz); + if (ext_div) { + *ext_div = DIV_ROUND_UP(no, max_no); + no = DIV_ROUND_UP(no, *ext_div); + } + + /* only even divisors (and 1) are supported */ + if (no > 1) + no = DIV_ROUND_UP(no, 2) * 2; + + vco_khz = freq_khz * no; + if (ext_div) + vco_khz *= *ext_div; + + if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) { + printf("%s: Cannot find out a supported VCO for Frequency (%luHz).\n", + __func__, freq_hz); + return -1; + } + + div->no = no; + + best_diff_khz = vco_khz; + for (nr = 1; nr < max_nr && best_diff_khz; nr++) { + fref_khz = ref_khz / nr; + if (fref_khz < FREF_MIN_KHZ) + break; + if (fref_khz > FREF_MAX_KHZ) + continue; + + nf = vco_khz / fref_khz; + if (nf >= max_nf) + continue; + diff_khz = vco_khz - nf * fref_khz; + if (nf + 1 < max_nf && diff_khz > fref_khz / 2) { + nf++; + diff_khz = fref_khz - diff_khz; + } + + if (diff_khz >= best_diff_khz) + continue; + + best_diff_khz = diff_khz; + div->nr = nr; + div->nf = nf; + } + + if (best_diff_khz > 4 * 1000) { + printf("%s: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n", + __func__, freq_hz, best_diff_khz * 1000); + return -EINVAL; + } + + return 0; +} + +static int rockchip_mac_set_clk(struct rk3288_cru *cru, + int periph, uint freq) +{ + /* Assuming mac_clk is fed by an external clock */ + rk_clrsetreg(&cru->cru_clksel_con[21], + RMII_EXTCLK_MASK << RMII_EXTCLK_SHIFT, + RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT); + + return 0; +} + +static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf, + int periph, unsigned int rate_hz) +{ + struct pll_div npll_config = {0}; + u32 lcdc_div; + int ret; + + ret = pll_para_config(rate_hz, &npll_config, &lcdc_div); + if (ret) + return ret; + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_SLOW << NPLL_MODE_SHIFT); + rkclk_set_pll(cru, CLK_NEW, &npll_config); + + /* waiting for pll lock */ + while (1) { + if (readl(&grf->soc_status[1]) & SOCSTS_NPLL_LOCK) + break; + udelay(1); + } + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_NORMAL << NPLL_MODE_SHIFT); + + /* vop dclk source clk: npll,dclk_div: 1 */ + switch (periph) { + case DCLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[27], 0xff << 8 | 3 << 0, + (lcdc_div - 1) << 8 | 2 << 0); + break; + case DCLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[29], 0xff << 8 | 3 << 6, + (lcdc_div - 1) << 8 | 2 << 6); + break; + } + + return 0; +} +#endif + +#ifdef CONFIG_SPL_BUILD +static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) +{ + u32 aclk_div; + u32 hclk_div; + u32 pclk_div; + + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + CPLL_MODE_MASK << CPLL_MODE_SHIFT, + GPLL_MODE_SLOW << GPLL_MODE_SHIFT | + CPLL_MODE_SLOW << CPLL_MODE_SHIFT); + + /* init pll */ + rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); + rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); + + /* waiting for pll lock */ + while ((readl(&grf->soc_status[1]) & + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) != + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) + udelay(1); + + /* + * pd_bus clock pll source selection and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1; + assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1; + assert((hclk_div + 1) * PD_BUS_HCLK_HZ == + PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2)); + + pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1; + assert((pclk_div + 1) * PD_BUS_PCLK_HZ == + PD_BUS_ACLK_HZ && pclk_div < 0x7); + + rk_clrsetreg(&cru->cru_clksel_con[1], + PD_BUS_PCLK_DIV_MASK << PD_BUS_PCLK_DIV_SHIFT | + PD_BUS_HCLK_DIV_MASK << PD_BUS_HCLK_DIV_SHIFT | + PD_BUS_ACLK_DIV0_MASK << PD_BUS_ACLK_DIV0_SHIFT | + PD_BUS_ACLK_DIV1_MASK << PD_BUS_ACLK_DIV1_SHIFT, + pclk_div << PD_BUS_PCLK_DIV_SHIFT | + hclk_div << PD_BUS_HCLK_DIV_SHIFT | + aclk_div << PD_BUS_ACLK_DIV0_SHIFT | + 0 << 0); + + /* + * peri clock pll source selection and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; + assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + + hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); + assert((1 << hclk_div) * PERI_HCLK_HZ == + PERI_ACLK_HZ && (hclk_div < 0x4)); + + pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); + assert((1 << pclk_div) * PERI_PCLK_HZ == + PERI_ACLK_HZ && (pclk_div < 0x4)); + + rk_clrsetreg(&cru->cru_clksel_con[10], + PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | + PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | + PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, + PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | + pclk_div << PERI_PCLK_DIV_SHIFT | + hclk_div << PERI_HCLK_DIV_SHIFT | + aclk_div << PERI_ACLK_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + CPLL_MODE_MASK << CPLL_MODE_SHIFT, + GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | + CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); +} +#endif + +void rk3288_clk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf) +{ + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_SLOW << APLL_MODE_SHIFT); + + rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); + + /* waiting for pll lock */ + while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK)) + udelay(1); + + /* + * core clock pll source selection and + * set up dependent divisors for MPAXI/M0AXI and ARM clocks. + * core clock select apll, apll clk = 1800MHz + * arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[0], + CORE_SEL_PLL_MASK << CORE_SEL_PLL_SHIFT | + A17_DIV_MASK << A17_DIV_SHIFT | + MP_DIV_MASK << MP_DIV_SHIFT | + M0_DIV_MASK << M0_DIV_SHIFT, + 0 << A17_DIV_SHIFT | + 3 << MP_DIV_SHIFT | + 1 << M0_DIV_SHIFT); + + /* + * set up dependent divisors for L2RAM/ATCLK and PCLK clocks. + * l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[37], + CLK_L2RAM_DIV_MASK << CLK_L2RAM_DIV_SHIFT | + ATCLK_CORE_DIV_CON_MASK << ATCLK_CORE_DIV_CON_SHIFT | + PCLK_CORE_DBG_DIV_MASK >> PCLK_CORE_DBG_DIV_SHIFT, + 1 << CLK_L2RAM_DIV_SHIFT | + 3 << ATCLK_CORE_DIV_CON_SHIFT | + 3 << PCLK_CORE_DBG_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_NORMAL << APLL_MODE_SHIFT); +} + +/* Get pll rate by id */ +static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, + enum rk_clk_id clk_id) +{ + uint32_t nr, no, nf; + uint32_t con; + int pll_id = rk_pll_id(clk_id); + struct rk3288_pll *pll = &cru->pll[pll_id]; + static u8 clk_shift[CLK_COUNT] = { + 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, + GPLL_MODE_SHIFT, NPLL_MODE_SHIFT + }; + uint shift; + + con = readl(&cru->cru_mode_con); + shift = clk_shift[clk_id]; + switch ((con >> shift) & APLL_MODE_MASK) { + case APLL_MODE_SLOW: + return OSC_HZ; + case APLL_MODE_NORMAL: + /* normal mode */ + con = readl(&pll->con0); + no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; + nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1; + con = readl(&pll->con1); + nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; + + return (24 * nf / (nr * no)) * 1000000; + case APLL_MODE_DEEP: + default: + return 32768; + } +} + +static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate, + int periph) +{ + uint src_rate; + uint div, mux; + u32 con; + + switch (periph) { + case HCLK_EMMC: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; + div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; + break; + case HCLK_SDMMC: + con = readl(&cru->cru_clksel_con[11]); + mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK; + div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; + break; + case HCLK_SDIO0: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> SDIO0_PLL_SHIFT) & SDIO0_PLL_MASK; + div = (con >> SDIO0_DIV_SHIFT) & SDIO0_DIV_MASK; + break; + default: + return -EINVAL; + } + + src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate; + return DIV_TO_RATE(src_rate, div); +} + +static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, + int periph, uint freq) +{ + int src_clk_div; + int mux; + + debug("%s: gclk_rate=%u\n", __func__, gclk_rate); + src_clk_div = RATE_TO_DIV(gclk_rate, freq); + + if (src_clk_div > 0x3f) { + src_clk_div = RATE_TO_DIV(OSC_HZ, freq); + mux = EMMC_PLL_SELECT_24MHZ; + assert((int)EMMC_PLL_SELECT_24MHZ == + (int)MMC0_PLL_SELECT_24MHZ); + } else { + mux = EMMC_PLL_SELECT_GENERAL; + assert((int)EMMC_PLL_SELECT_GENERAL == + (int)MMC0_PLL_SELECT_GENERAL); + } + switch (periph) { + case HCLK_EMMC: + rk_clrsetreg(&cru->cru_clksel_con[12], + EMMC_PLL_MASK << EMMC_PLL_SHIFT | + EMMC_DIV_MASK << EMMC_DIV_SHIFT, + mux << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + break; + case HCLK_SDMMC: + rk_clrsetreg(&cru->cru_clksel_con[11], + MMC0_PLL_MASK << MMC0_PLL_SHIFT | + MMC0_DIV_MASK << MMC0_DIV_SHIFT, + mux << MMC0_PLL_SHIFT | + (src_clk_div - 1) << MMC0_DIV_SHIFT); + break; + case HCLK_SDIO0: + rk_clrsetreg(&cru->cru_clksel_con[12], + SDIO0_PLL_MASK << SDIO0_PLL_SHIFT | + SDIO0_DIV_MASK << SDIO0_DIV_SHIFT, + mux << SDIO0_PLL_SHIFT | + (src_clk_div - 1) << SDIO0_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rockchip_mmc_get_clk(cru, gclk_rate, periph); +} + +static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint gclk_rate, + int periph) +{ + uint div, mux; + u32 con; + + switch (periph) { + case SCLK_SPI0: + con = readl(&cru->cru_clksel_con[25]); + mux = (con >> SPI0_PLL_SHIFT) & SPI0_PLL_MASK; + div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK; + break; + case SCLK_SPI1: + con = readl(&cru->cru_clksel_con[25]); + mux = (con >> SPI1_PLL_SHIFT) & SPI1_PLL_MASK; + div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK; + break; + case SCLK_SPI2: + con = readl(&cru->cru_clksel_con[39]); + mux = (con >> SPI2_PLL_SHIFT) & SPI2_PLL_MASK; + div = (con >> SPI2_DIV_SHIFT) & SPI2_DIV_MASK; + break; + default: + return -EINVAL; + } + assert(mux == SPI0_PLL_SELECT_GENERAL); + + return DIV_TO_RATE(gclk_rate, div); +} + +static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate, + int periph, uint freq) +{ + int src_clk_div; + + debug("%s: clk_general_rate=%u\n", __func__, gclk_rate); + src_clk_div = RATE_TO_DIV(gclk_rate, freq); + switch (periph) { + case SCLK_SPI0: + rk_clrsetreg(&cru->cru_clksel_con[25], + SPI0_PLL_MASK << SPI0_PLL_SHIFT | + SPI0_DIV_MASK << SPI0_DIV_SHIFT, + SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT | + src_clk_div << SPI0_DIV_SHIFT); + break; + case SCLK_SPI1: + rk_clrsetreg(&cru->cru_clksel_con[25], + SPI1_PLL_MASK << SPI1_PLL_SHIFT | + SPI1_DIV_MASK << SPI1_DIV_SHIFT, + SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT | + src_clk_div << SPI1_DIV_SHIFT); + break; + case SCLK_SPI2: + rk_clrsetreg(&cru->cru_clksel_con[39], + SPI2_PLL_MASK << SPI2_PLL_SHIFT | + SPI2_DIV_MASK << SPI2_DIV_SHIFT, + SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT | + src_clk_div << SPI2_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rockchip_spi_get_clk(cru, gclk_rate, periph); +} + +static ulong rk3288_clk_get_rate(struct clk *clk) +{ + struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); + ulong new_rate, gclk_rate; + + gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); + switch (clk->id) { + case 0 ... 63: + new_rate = rkclk_pll_get_rate(priv->cru, clk->id); + break; + case HCLK_EMMC: + case HCLK_SDMMC: + case HCLK_SDIO0: + new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id); + break; + case SCLK_SPI0: + case SCLK_SPI1: + case SCLK_SPI2: + new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, clk->id); + break; + case PCLK_I2C0: + case PCLK_I2C1: + case PCLK_I2C2: + case PCLK_I2C3: + case PCLK_I2C4: + case PCLK_I2C5: + return gclk_rate; + default: + return -ENOENT; + } + + return new_rate; +} + +static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3288_cru *cru = priv->cru; + ulong new_rate, gclk_rate; + + gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); + switch (clk->id) { + case CLK_DDR: + new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate); + break; + case HCLK_EMMC: + case HCLK_SDMMC: + case HCLK_SDIO0: + new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate); + break; + case SCLK_SPI0: + case SCLK_SPI1: + case SCLK_SPI2: + new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate); + break; +#ifndef CONFIG_SPL_BUILD + case SCLK_MAC: + new_rate = rockchip_mac_set_clk(priv->cru, clk->id, rate); + break; + case DCLK_VOP0: + case DCLK_VOP1: + new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate); + break; + case SCLK_EDP_24M: + /* clk_edp_24M source: 24M */ + rk_setreg(&cru->cru_clksel_con[28], 1 << 15); + + /* rst edp */ + rk_setreg(&cru->cru_clksel_con[6], 1 << 15); + udelay(1); + rk_clrreg(&cru->cru_clksel_con[6], 1 << 15); + new_rate = rate; + break; + case ACLK_VOP0: + case ACLK_VOP1: { + u32 div; + + /* vop aclk source clk: cpll */ + div = CPLL_HZ / rate; + assert((div - 1 < 64) && (div * rate == CPLL_HZ)); + + switch (clk->id) { + case ACLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 6 | 0x1f << 0, + 0 << 6 | (div - 1) << 0); + break; + case ACLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 14 | 0x1f << 8, + 0 << 14 | (div - 1) << 8); + break; + } + new_rate = rate; + break; + } + case PCLK_HDMI_CTRL: + /* enable pclk hdmi ctrl */ + rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9); + + /* software reset hdmi */ + rk_setreg(&cru->cru_clkgate_con[7], 1 << 9); + udelay(1); + rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9); + new_rate = rate; + break; +#endif + default: + return -ENOENT; + } + + return new_rate; +} + +static struct clk_ops rk3288_clk_ops = { + .get_rate = rk3288_clk_get_rate, + .set_rate = rk3288_clk_set_rate, +}; + +static int rk3288_clk_ofdata_to_platdata(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3288_clk_priv *priv = dev_get_priv(dev); + + priv->cru = (struct rk3288_cru *)dev_get_addr(dev); +#endif + + return 0; +} + +static int rk3288_clk_probe(struct udevice *dev) +{ + struct rk3288_clk_priv *priv = dev_get_priv(dev); + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(priv->grf)) + return PTR_ERR(priv->grf); +#ifdef CONFIG_SPL_BUILD +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + + priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); +#endif + rkclk_init(priv->cru, priv->grf); +#endif + + return 0; +} + +static int rk3288_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, "rk3288_sysreset", "reset", &dev); + if (ret) + debug("Warning: No RK3288 reset driver: ret=%d\n", ret); + + return 0; +} + +static const struct udevice_id rk3288_clk_ids[] = { + { .compatible = "rockchip,rk3288-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3288_cru) = { + .name = "rockchip_rk3288_cru", + .id = UCLASS_CLK, + .of_match = rk3288_clk_ids, + .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv), + .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat), + .ops = &rk3288_clk_ops, + .bind = rk3288_clk_bind, + .ofdata_to_platdata = rk3288_clk_ofdata_to_platdata, + .probe = rk3288_clk_probe, +}; diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c new file mode 100644 index 0000000..c0bb80c --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -0,0 +1,819 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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_SHIFT = 12, + PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, + PLL_POSTDIV1_SHIFT = 8, + PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT, + PLL_REFDIV_MASK = 0x3f, + PLL_REFDIV_SHIFT = 0, + + /* PLL_CON2 */ + PLL_LOCK_STATUS_SHIFT = 31, + PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, + PLL_FRACDIV_MASK = 0xffffff, + PLL_FRACDIV_SHIFT = 0, + + /* PLL_CON3 */ + PLL_MODE_SHIFT = 8, + PLL_MODE_MASK = 3 << PLL_MODE_SHIFT, + PLL_MODE_SLOW = 0, + PLL_MODE_NORM, + PLL_MODE_DEEP, + PLL_DSMPD_SHIFT = 3, + PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, + 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_SHIFT = 7, + SPI3_PLL_SEL_MASK = 1 << SPI3_PLL_SEL_SHIFT, + SPI3_PLL_SEL_24M = 0, + SPI3_PLL_SEL_PPLL = 1, + SPI3_DIV_CON_SHIFT = 0x0, + SPI3_DIV_CON_MASK = 0x7f, + + /* 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_SHIFT = 8, + ACLKM_CORE_L_DIV_CON_MASK = 0x1f << ACLKM_CORE_L_DIV_CON_SHIFT, + CLK_CORE_L_PLL_SEL_SHIFT = 6, + CLK_CORE_L_PLL_SEL_MASK = 3 << CLK_CORE_L_PLL_SEL_SHIFT, + 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_SHIFT = 0x8, + PCLK_DBG_L_DIV_MASK = 0x1f << PCLK_DBG_L_DIV_SHIFT, + ATCLK_CORE_L_DIV_SHIFT = 0, + ATCLK_CORE_L_DIV_MASK = 0x1f << ATCLK_CORE_L_DIV_SHIFT, + + /* CLKSEL_CON14 */ + PCLK_PERIHP_DIV_CON_SHIFT = 12, + PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, + HCLK_PERIHP_DIV_CON_SHIFT = 8, + HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT, + ACLK_PERIHP_PLL_SEL_SHIFT = 7, + ACLK_PERIHP_PLL_SEL_MASK = 1 << ACLK_PERIHP_PLL_SEL_SHIFT, + ACLK_PERIHP_PLL_SEL_CPLL = 0, + ACLK_PERIHP_PLL_SEL_GPLL = 1, + ACLK_PERIHP_DIV_CON_SHIFT = 0, + ACLK_PERIHP_DIV_CON_MASK = 0x1f, + + /* CLKSEL_CON21 */ + ACLK_EMMC_PLL_SEL_SHIFT = 7, + ACLK_EMMC_PLL_SEL_MASK = 0x1 << ACLK_EMMC_PLL_SEL_SHIFT, + ACLK_EMMC_PLL_SEL_GPLL = 0x1, + ACLK_EMMC_DIV_CON_SHIFT = 0, + ACLK_EMMC_DIV_CON_MASK = 0x1f, + + /* CLKSEL_CON22 */ + CLK_EMMC_PLL_SHIFT = 8, + CLK_EMMC_PLL_MASK = 0x7 << CLK_EMMC_PLL_SHIFT, + CLK_EMMC_PLL_SEL_GPLL = 0x1, + CLK_EMMC_DIV_CON_SHIFT = 0, + CLK_EMMC_DIV_CON_MASK = 0x7f << CLK_EMMC_DIV_CON_SHIFT, + + /* CLKSEL_CON23 */ + PCLK_PERILP0_DIV_CON_SHIFT = 12, + PCLK_PERILP0_DIV_CON_MASK = 0x7 << PCLK_PERILP0_DIV_CON_SHIFT, + HCLK_PERILP0_DIV_CON_SHIFT = 8, + HCLK_PERILP0_DIV_CON_MASK = 3 << HCLK_PERILP0_DIV_CON_SHIFT, + ACLK_PERILP0_PLL_SEL_SHIFT = 7, + ACLK_PERILP0_PLL_SEL_MASK = 1 << ACLK_PERILP0_PLL_SEL_SHIFT, + ACLK_PERILP0_PLL_SEL_CPLL = 0, + ACLK_PERILP0_PLL_SEL_GPLL = 1, + ACLK_PERILP0_DIV_CON_SHIFT = 0, + ACLK_PERILP0_DIV_CON_MASK = 0x1f, + + /* CLKSEL_CON25 */ + PCLK_PERILP1_DIV_CON_SHIFT = 8, + PCLK_PERILP1_DIV_CON_MASK = 0x7 << PCLK_PERILP1_DIV_CON_SHIFT, + HCLK_PERILP1_PLL_SEL_SHIFT = 7, + HCLK_PERILP1_PLL_SEL_MASK = 1 << HCLK_PERILP1_PLL_SEL_SHIFT, + HCLK_PERILP1_PLL_SEL_CPLL = 0, + HCLK_PERILP1_PLL_SEL_GPLL = 1, + HCLK_PERILP1_DIV_CON_SHIFT = 0, + HCLK_PERILP1_DIV_CON_MASK = 0x1f, + + /* CLKSEL_CON26 */ + CLK_SARADC_DIV_CON_SHIFT = 8, + CLK_SARADC_DIV_CON_MASK = 0xff << CLK_SARADC_DIV_CON_SHIFT, + + /* CLKSEL_CON27 */ + CLK_TSADC_SEL_X24M = 0x0, + CLK_TSADC_SEL_SHIFT = 15, + CLK_TSADC_SEL_MASK = 1 << CLK_TSADC_SEL_SHIFT, + CLK_TSADC_DIV_CON_SHIFT = 0, + CLK_TSADC_DIV_CON_MASK = 0x3ff, + + /* CLKSEL_CON47 & CLKSEL_CON48 */ + ACLK_VOP_PLL_SEL_SHIFT = 6, + ACLK_VOP_PLL_SEL_MASK = 0x3 << ACLK_VOP_PLL_SEL_SHIFT, + ACLK_VOP_PLL_SEL_CPLL = 0x1, + ACLK_VOP_DIV_CON_SHIFT = 0, + ACLK_VOP_DIV_CON_MASK = 0x1f << ACLK_VOP_DIV_CON_SHIFT, + + /* CLKSEL_CON49 & CLKSEL_CON50 */ + DCLK_VOP_DCLK_SEL_SHIFT = 11, + DCLK_VOP_DCLK_SEL_MASK = 1 << DCLK_VOP_DCLK_SEL_SHIFT, + DCLK_VOP_DCLK_SEL_DIVOUT = 0, + DCLK_VOP_PLL_SEL_SHIFT = 8, + DCLK_VOP_PLL_SEL_MASK = 3 << DCLK_VOP_PLL_SEL_SHIFT, + 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_SHIFT = 8, + RESETN_DDR0_REQ_MASK = 1 << RESETN_DDR0_REQ_SHIFT, + RESETN_DDRPHY0_REQ_SHIFT = 9, + RESETN_DDRPHY0_REQ_MASK = 1 << RESETN_DDRPHY0_REQ_SHIFT, + RESETN_DDR1_REQ_SHIFT = 12, + RESETN_DDR1_REQ_MASK = 1 << RESETN_DDR1_REQ_SHIFT, + RESETN_DDRPHY1_REQ_SHIFT = 13, + RESETN_DDRPHY1_REQ_MASK = 1 << RESETN_DDRPHY1_REQ_SHIFT, +}; + +#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 restructions of pll in integer mode, 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; + + 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_SLOW << PLL_MODE_SHIFT); + + /* use integer mode */ + rk_clrsetreg(&pll_con[3], PLL_DSMPD_MASK, + PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); + + rk_clrsetreg(&pll_con[0], PLL_FBDIV_MASK, + div->fbdiv << PLL_FBDIV_SHIFT); + rk_clrsetreg(&pll_con[1], + PLL_POSTDIV2_MASK | PLL_POSTDIV1_MASK | + 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_NORM << PLL_MODE_SHIFT); +} + +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 three lines as a fix of bootrom bug. + */ + 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 | HCLK_PERIHP_DIV_CON_MASK | + ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK, + 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 | HCLK_PERILP0_DIV_CON_MASK | + ACLK_PERILP0_PLL_SEL_MASK | ACLK_PERILP0_DIV_CON_MASK, + 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 | HCLK_PERILP1_DIV_CON_MASK | + HCLK_PERILP1_PLL_SEL_MASK, + 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 | CLK_CORE_L_PLL_SEL_MASK | + CLK_CORE_L_DIV_MASK, + 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 | ATCLK_CORE_L_DIV_MASK, + 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_DIV_CON_MASK, + 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_PLL_SEL_MASK| + DCLK_VOP_DIV_CON_MASK, + 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_DIV_CON_MASK, + 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_DIV_CON_MASK, + 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_DIV_CON_MASK, + 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); + ulong ret = 0; + + switch (clk->id) { + case 0 ... 63: + return 0; + case SCLK_SDMMC: + case SCLK_EMMC: + ret = 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: + ret = rk3399_i2c_set_clk(priv->cru, clk->id, rate); + break; + case DCLK_VOP0: + case DCLK_VOP1: + rate = rk3399_vop_set_clk(priv->cru, clk->id, rate); + break; + default: + return -ENOENT; + } + + return ret; +} + +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, "clk_rk3399", &dev); + if (ret) + return ERR_PTR(ret); + + addr = dev_get_addr_ptr(dev); + if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) + return ERR_PTR(-EINVAL); + + return addr; +} + +static int rk3399_clk_probe(struct udevice *dev) +{ + struct rk3399_clk_priv *priv = dev_get_priv(dev); + + rkclk_init(priv->cru); + + return 0; +} + +static int rk3399_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3399_clk_priv *priv = dev_get_priv(dev); + + priv->cru = (struct rk3399_cru *)dev_get_addr(dev); + + 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), + .ofdata_to_platdata = rk3399_clk_ofdata_to_platdata, + .ops = &rk3399_clk_ops, + .bind = rk3399_clk_bind, + .probe = rk3399_clk_probe, +}; -- cgit v0.10.2