From: Lin Huang <hl@rock-chips.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v3 08/17] rockchip: rk3036: Add clock driver
Date: Fri, 6 Nov 2015 17:33:22 +0800 [thread overview]
Message-ID: <1446802414-31132-9-git-send-email-hl@rock-chips.com> (raw)
In-Reply-To: <1446802414-31132-1-git-send-email-hl@rock-chips.com>
Add a driver for setting up and modifying the various PLLs, peripheral
clocks and mmc clocks on RK3036
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v1:
- clean copyright announcement
Changes in v2:
- move some macro to cru_rk3036.h
Changes in v3: None
arch/arm/include/asm/arch-rockchip/cru_rk3036.h | 168 ++++++++++
drivers/clk/Makefile | 1 +
drivers/clk/clk_rk3036.c | 414 ++++++++++++++++++++++++
3 files changed, 583 insertions(+)
create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3036.h
create mode 100644 drivers/clk/clk_rk3036.c
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3036.h b/arch/arm/include/asm/arch-rockchip/cru_rk3036.h
new file mode 100644
index 0000000..7ecc8ee
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3036.h
@@ -0,0 +1,168 @@
+/*
+ * (C) Copyright 2015 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#ifndef _ASM_ARCH_CRU_RK3036_H
+#define _ASM_ARCH_CRU_RK3036_H
+
+#include <common.h>
+
+#define OSC_HZ (24 * 1000 * 1000)
+
+#define APLL_HZ (600 * 1000000)
+#define GPLL_HZ (594 * 1000000)
+
+#define CORE_PERI_HZ 150000000
+#define CORE_ACLK_HZ 300000000
+
+#define CPU_ACLK_HZ 150000000
+#define CPU_HCLK_HZ 300000000
+#define CPU_PCLK_HZ 300000000
+
+#define PERI_ACLK_HZ 148500000
+#define PERI_HCLK_HZ 148500000
+#define PERI_PCLK_HZ 74250000
+
+struct rk3036_cru {
+ struct rk3036_pll {
+ unsigned int con0;
+ unsigned int con1;
+ unsigned int con2;
+ unsigned int con3;
+ } pll[4];
+ unsigned int cru_mode_con;
+ unsigned int cru_clksel_con[35];
+ unsigned int cru_clkgate_con[11];
+ unsigned int reserved;
+ unsigned int cru_glb_srst_fst_value;
+ unsigned int cru_glb_srst_snd_value;
+ unsigned int reserved1[2];
+ unsigned int cru_softrst_con[9];
+ unsigned int cru_misc_con;
+ unsigned int reserved2[2];
+ unsigned int cru_glb_cnt_th;
+ unsigned int cru_sdmmc_con[2];
+ unsigned int cru_sdio_con[2];
+ unsigned int cru_emmc_con[2];
+ unsigned int reserved3;
+ unsigned int cru_rst_st;
+ unsigned int reserved4[0x23];
+ unsigned int cru_pll_mask_con;
+};
+check_member(rk3036_cru, cru_pll_mask_con, 0x01f0);
+
+struct pll_div {
+ u32 refdiv;
+ u32 fbdiv;
+ u32 postdiv1;
+ u32 postdiv2;
+ u32 frac;
+};
+
+enum {
+ /* PLLCON0*/
+ PLL_POSTDIV1_MASK = 7,
+ PLL_POSTDIV1_SHIFT = 12,
+ PLL_FBDIV_MASK = 0xfff,
+ PLL_FBDIV_SHIFT = 0,
+
+ /* PLLCON1 */
+ PLL_DSMPD_MASK = 1,
+ PLL_DSMPD_SHIFT = 12,
+ PLL_LOCK_STATUS_MASK = 1,
+ PLL_LOCK_STATUS_SHIFT = 10,
+ PLL_POSTDIV2_MASK = 7,
+ PLL_POSTDIV2_SHIFT = 6,
+ PLL_REFDIV_MASK = 0x3f,
+ PLL_REFDIV_SHIFT = 0,
+ PLL_RST_SHIFT = 14,
+
+ /* CRU_MODE */
+ GPLL_MODE_MASK = 3,
+ GPLL_MODE_SHIFT = 12,
+ GPLL_MODE_SLOW = 0,
+ GPLL_MODE_NORM,
+ GPLL_MODE_DEEP,
+ DPLL_MODE_MASK = 1,
+ DPLL_MODE_SHIFT = 4,
+ DPLL_MODE_SLOW = 0,
+ DPLL_MODE_NORM,
+ APLL_MODE_MASK = 1,
+ APLL_MODE_SHIFT = 0,
+ APLL_MODE_SLOW = 0,
+ APLL_MODE_NORM,
+
+ /* CRU_CLK_SEL0_CON */
+ CPU_CLK_PLL_SEL_MASK = 3,
+ CPU_CLK_PLL_SEL_SHIFT = 14,
+ CPU_CLK_PLL_SEL_APLL = 0,
+ CPU_CLK_PLL_SEL_DPLL,
+ CPU_CLK_PLL_SEL_GPLL,
+ ACLK_CPU_DIV_MASK = 0x1f,
+ ACLK_CPU_DIV_SHIFT = 8,
+ CORE_CLK_PLL_SEL_MASK = 1,
+ CORE_CLK_PLL_SEL_SHIFT = 7,
+ CORE_CLK_PLL_SEL_APLL = 0,
+ CORE_CLK_PLL_SEL_GPLL,
+ CORE_DIV_CON_MASK = 0x1f,
+ CORE_DIV_CON_SHIFT = 0,
+
+ /* CRU_CLK_SEL1_CON */
+ CPU_PCLK_DIV_MASK = 7,
+ CPU_PCLK_DIV_SHIFT = 12,
+ CPU_HCLK_DIV_MASK = 3,
+ CPU_HCLK_DIV_SHIFT = 8,
+ CORE_ACLK_DIV_MASK = 7,
+ CORE_ACLK_DIV_SHIFT = 4,
+ CORE_PERI_DIV_MASK = 0xf,
+ CORE_PERI_DIV_SHIFT = 0,
+
+ /* CRU_CLKSEL10_CON */
+ PERI_PLL_SEL_MASK = 3,
+ PERI_PLL_SEL_SHIFT = 14,
+ PERI_PLL_APLL = 0,
+ PERI_PLL_DPLL,
+ PERI_PLL_GPLL,
+ PERI_PCLK_DIV_MASK = 3,
+ PERI_PCLK_DIV_SHIFT = 12,
+ PERI_HCLK_DIV_MASK = 3,
+ PERI_HCLK_DIV_SHIFT = 8,
+ PERI_ACLK_DIV_MASK = 0x1f,
+ PERI_ACLK_DIV_SHIFT = 0,
+
+ /* CRU_CLKSEL11_CON */
+ SDIO_DIV_MASK = 0x7f,
+ SDIO_DIV_SHIFT = 8,
+ MMC0_DIV_MASK = 0x7f,
+ MMC0_DIV_SHIFT = 0,
+
+ /* CRU_CLKSEL12_CON */
+ EMMC_PLL_MASK = 3,
+ EMMC_PLL_SHIFT = 12,
+ EMMC_SEL_APLL = 0,
+ EMMC_SEL_DPLL,
+ EMMC_SEL_GPLL,
+ EMMC_SEL_24M,
+ SDIO_PLL_MASK = 3,
+ SDIO_PLL_SHIFT = 10,
+ SDIO_SEL_APLL = 0,
+ SDIO_SEL_DPLL,
+ SDIO_SEL_GPLL,
+ SDIO_SEL_24M,
+ MMC0_PLL_MASK = 3,
+ MMC0_PLL_SHIFT = 8,
+ MMC0_SEL_APLL = 0,
+ MMC0_SEL_DPLL,
+ MMC0_SEL_GPLL,
+ MMC0_SEL_24M,
+ EMMC_DIV_MASK = 0x7f,
+ EMMC_DIV_SHIFT = 0,
+
+ /* CRU_SOFTRST5_CON */
+ DDRCTRL_PSRST_SHIFT = 11,
+ DDRCTRL_SRST_SHIFT = 10,
+ DDRPHY_PSRST_SHIFT = 9,
+ DDRPHY_SRST_SHIFT = 8,
+};
+#endif
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 008ec10..4a6a4a8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -6,5 +6,6 @@
#
obj-$(CONFIG_CLK) += clk-uclass.o
+obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
diff --git a/drivers/clk/clk_rk3036.c b/drivers/clk/clk_rk3036.c
new file mode 100644
index 0000000..6c802b6
--- /dev/null
+++ b/drivers/clk/clk_rk3036.c
@@ -0,0 +1,414 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3036.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rk3036_clk_plat {
+ enum rk_clk_id clk_id;
+};
+
+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;
+}
+
+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,
+ enum periph_id periph)
+{
+ uint src_rate;
+ uint div, mux;
+ u32 con;
+
+ switch (periph) {
+ case PERIPH_ID_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 PERIPH_ID_SDCARD:
+ 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,
+ enum periph_id 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 PERIPH_ID_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 PERIPH_ID_SDCARD:
+ 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 udevice *dev)
+{
+ struct rk3036_clk_plat *plat = dev_get_platdata(dev);
+ struct rk3036_clk_priv *priv = dev_get_priv(dev);
+
+ debug("%s\n", dev->name);
+ return rkclk_pll_get_rate(priv->cru, plat->clk_id);
+}
+
+static ulong rk3036_clk_set_rate(struct udevice *dev, ulong rate)
+{
+ debug("%s\n", dev->name);
+
+ return 0;
+}
+
+ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+{
+ struct rk3036_clk_priv *priv = dev_get_priv(dev);
+ ulong new_rate;
+
+ switch (periph) {
+ case PERIPH_ID_EMMC:
+ new_rate = rockchip_mmc_set_clk(priv->cru, clk_get_rate(dev),
+ periph, 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,
+ .set_periph_rate = rk3036_set_periph_rate,
+};
+
+static int rk3036_clk_probe(struct udevice *dev)
+{
+ struct rk3036_clk_plat *plat = dev_get_platdata(dev);
+ struct rk3036_clk_priv *priv = dev_get_priv(dev);
+
+ if (plat->clk_id != CLK_OSC) {
+ struct rk3036_clk_priv *parent_priv = dev_get_priv(dev->parent);
+
+ priv->cru = parent_priv->cru;
+ return 0;
+ }
+ priv->cru = (struct rk3036_cru *)dev_get_addr(dev);
+ rkclk_init(priv->cru);
+
+ return 0;
+}
+
+static const char *const clk_name[] = {
+ "osc",
+ "apll",
+ "dpll",
+ "cpll",
+ "gpll",
+ "mpll",
+};
+
+static int rk3036_clk_bind(struct udevice *dev)
+{
+ struct rk3036_clk_plat *plat = dev_get_platdata(dev);
+ int pll, ret;
+
+ /* We only need to set up the root clock */
+ if (dev->of_offset == -1) {
+ plat->clk_id = CLK_OSC;
+ return 0;
+ }
+
+ /* Create devices for P main clocks */
+ for (pll = 1; pll < CLK_COUNT; pll++) {
+ struct udevice *child;
+ struct rk3036_clk_plat *cplat;
+
+ debug("%s %s\n", __func__, clk_name[pll]);
+ ret = device_bind_driver(dev, "clk_rk3036", clk_name[pll],
+ &child);
+ if (ret)
+ return ret;
+
+ cplat = dev_get_platdata(child);
+ cplat->clk_id = pll;
+ }
+
+ /* The reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(gd->dm_root, "rk3036_reset", "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),
+ .platdata_auto_alloc_size = sizeof(struct rk3036_clk_plat),
+ .ops = &rk3036_clk_ops,
+ .bind = rk3036_clk_bind,
+ .probe = rk3036_clk_probe,
+};
--
1.9.1
next prev parent reply other threads:[~2015-11-06 9:33 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-11-06 9:33 [U-Boot] [PATCH v3 00/20] Bring up rk3036 uboot Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 01/17] rockchip: add timer driver Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 02/17] rockchip: move SYS_MALLOC_F_LEN to rk3288 own Kconfig Lin Huang
2015-11-06 12:08 ` Simon Glass
2015-11-09 2:59 ` hl
2015-11-09 4:46 ` Simon Glass
2015-11-09 4:51 ` hl
2015-11-06 9:33 ` [U-Boot] [PATCH v3 03/17] rockchip: rename board-spl.c to rk3288-board-spl.c Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 04/17] rockchip: add config decide whether to build common.c Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 05/17] dm: core: Add SPL Kconfig for REGMAP and SYSCON Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 06/17] rockchip: serial driver support rk3036 Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 07/17] rockchip: Bring in RK3036 device tree file includes and bindings Lin Huang
2015-11-06 9:33 ` Lin Huang [this message]
2015-11-06 9:33 ` [U-Boot] [PATCH v3 09/17] rockchip: rk3036: Add header files for GRF Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 10/17] rockchip: rk3036: Add Soc reset driver Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 11/17] rockchip: rk3036: Add a simple syscon driver Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 12/17] rockchip: rk3036: Add pinctrl driver Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 13/17] rockchip: Add an rk3036 MMC driver Lin Huang
2015-11-06 9:33 ` [U-Boot] [PATCH v3 14/17] rockchip: add early uart driver Lin Huang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1446802414-31132-9-git-send-email-hl@rock-chips.com \
--to=hl@rock-chips.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.