/*
* (C) Copyright 2015 Google, Inc
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
+#include <dt-structs.h>
#include <errno.h>
+#include <mapmem.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
DECLARE_GLOBAL_DATA_PTR;
-struct rk3399_pmuclk_priv {
- struct rk3399_pmucru *pmucru;
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+struct rk3399_clk_plat {
+ struct dtd_rockchip_rk3399_cru dtd;
};
+struct rk3399_pmuclk_plat {
+ struct dtd_rockchip_rk3399_pmucru dtd;
+};
+#endif
+
struct pll_div {
u32 refdiv;
u32 fbdiv;
.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};
+#if defined(CONFIG_SPL_BUILD)
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);
+#else
static const struct pll_div ppll_init_cfg = PLL_DIVISORS(PPLL_HZ, 2, 2, 1);
+#endif
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);
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,
+ CLK_SPI_PLL_SEL_WIDTH = 1,
+ CLK_SPI_PLL_SEL_MASK = ((1 < CLK_SPI_PLL_SEL_WIDTH) - 1),
+ CLK_SPI_PLL_SEL_CPLL = 0,
+ CLK_SPI_PLL_SEL_GPLL = 1,
+ CLK_SPI_PLL_DIV_CON_WIDTH = 7,
+ CLK_SPI_PLL_DIV_CON_MASK = ((1 << CLK_SPI_PLL_DIV_CON_WIDTH) - 1),
+
+ CLK_SPI5_PLL_DIV_CON_SHIFT = 8,
+ CLK_SPI5_PLL_SEL_SHIFT = 15,
/* CLKSEL_CON59 */
CLK_SPI1_PLL_SEL_SHIFT = 15,
return 0;
}
+#ifdef CONFIG_SPL_BUILD
static void rkclk_init(struct rk3399_cru *cru)
{
u32 aclk_div;
hclk_div << HCLK_PERILP1_DIV_CON_SHIFT |
HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT);
}
+#endif
void rk3399_configure_cpu(struct rk3399_cru *cru,
enum apll_l_frequencies apll_l_freq)
return -EINVAL;
}
- return DIV_TO_RATE(GPLL_HZ, src_clk_div);
+ return rk3399_i2c_get_clk(cru, clk_id);
+}
+
+/*
+ * RK3399 SPI clocks have a common divider-width (7 bits) and a single bit
+ * to select either CPLL or GPLL as the clock-parent. The location within
+ * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable.
+ */
+
+struct spi_clkreg {
+ uint8_t reg; /* CLKSEL_CON[reg] register in CRU */
+ uint8_t div_shift;
+ uint8_t sel_shift;
+};
+
+/*
+ * The entries are numbered relative to their offset from SCLK_SPI0.
+ *
+ * Note that SCLK_SPI3 (which is configured via PMUCRU and requires different
+ * logic is not supported).
+ */
+static const struct spi_clkreg spi_clkregs[] = {
+ [0] = { .reg = 59,
+ .div_shift = CLK_SPI0_PLL_DIV_CON_SHIFT,
+ .sel_shift = CLK_SPI0_PLL_SEL_SHIFT, },
+ [1] = { .reg = 59,
+ .div_shift = CLK_SPI1_PLL_DIV_CON_SHIFT,
+ .sel_shift = CLK_SPI1_PLL_SEL_SHIFT, },
+ [2] = { .reg = 60,
+ .div_shift = CLK_SPI2_PLL_DIV_CON_SHIFT,
+ .sel_shift = CLK_SPI2_PLL_SEL_SHIFT, },
+ [3] = { .reg = 60,
+ .div_shift = CLK_SPI4_PLL_DIV_CON_SHIFT,
+ .sel_shift = CLK_SPI4_PLL_SEL_SHIFT, },
+ [4] = { .reg = 58,
+ .div_shift = CLK_SPI5_PLL_DIV_CON_SHIFT,
+ .sel_shift = CLK_SPI5_PLL_SEL_SHIFT, },
+};
+
+static inline u32 extract_bits(u32 val, unsigned width, unsigned shift)
+{
+ return (val >> shift) & ((1 << width) - 1);
+}
+
+static ulong rk3399_spi_get_clk(struct rk3399_cru *cru, ulong clk_id)
+{
+ const struct spi_clkreg *spiclk = NULL;
+ u32 div, val;
+
+ switch (clk_id) {
+ case SCLK_SPI0 ... SCLK_SPI5:
+ spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
+ break;
+
+ default:
+ error("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
+ return -EINVAL;
+ }
+
+ val = readl(&cru->clksel_con[spiclk->reg]);
+ div = extract_bits(val, CLK_SPI_PLL_DIV_CON_WIDTH, spiclk->div_shift);
+
+ return DIV_TO_RATE(GPLL_HZ, div);
+}
+
+static ulong rk3399_spi_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz)
+{
+ const struct spi_clkreg *spiclk = NULL;
+ int src_clk_div;
+
+ src_clk_div = RATE_TO_DIV(GPLL_HZ, hz);
+ assert(src_clk_div < 127);
+
+ switch (clk_id) {
+ case SCLK_SPI1 ... SCLK_SPI5:
+ spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
+ break;
+
+ default:
+ error("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
+ return -EINVAL;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[spiclk->reg],
+ ((CLK_SPI_PLL_DIV_CON_MASK << spiclk->div_shift) |
+ (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift)),
+ ((src_clk_div << spiclk->div_shift) |
+ (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift)));
+
+ return rk3399_spi_get_clk(cru, clk_id);
}
static ulong rk3399_vop_set_clk(struct rk3399_cru *cru, ulong clk_id, u32 hz)
if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT
== CLK_EMMC_PLL_SEL_24M)
- return DIV_TO_RATE(24*1024*1024, div);
+ return DIV_TO_RATE(24*1000*1000, div);
else
return DIV_TO_RATE(GPLL_HZ, div);
}
if (src_clk_div > 127) {
/* use 24MHz source for 400KHz clock */
- src_clk_div = 24*1024*1024 / set_rate;
+ src_clk_div = 24*1000*1000 / set_rate;
rk_clrsetreg(&cru->clksel_con[16],
CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT |
return rk3399_mmc_get_clk(cru, clk_id);
}
+#define PMUSGRF_DDR_RGN_CON16 0xff330040
+static ulong rk3399_ddr_set_clk(struct rk3399_cru *cru,
+ ulong set_rate)
+{
+ struct pll_div dpll_cfg;
+
+ /* IC ECO bug, need to set this register */
+ writel(0xc000c000, PMUSGRF_DDR_RGN_CON16);
+
+ /* clk_ddrc == DPLL = 24MHz / refdiv * fbdiv / postdiv1 / postdiv2 */
+ switch (set_rate) {
+ case 200*MHz:
+ dpll_cfg = (struct pll_div)
+ {.refdiv = 1, .fbdiv = 50, .postdiv1 = 6, .postdiv2 = 1};
+ break;
+ case 300*MHz:
+ dpll_cfg = (struct pll_div)
+ {.refdiv = 2, .fbdiv = 100, .postdiv1 = 4, .postdiv2 = 1};
+ break;
+ case 666*MHz:
+ dpll_cfg = (struct pll_div)
+ {.refdiv = 2, .fbdiv = 111, .postdiv1 = 2, .postdiv2 = 1};
+ break;
+ case 800*MHz:
+ dpll_cfg = (struct pll_div)
+ {.refdiv = 1, .fbdiv = 100, .postdiv1 = 3, .postdiv2 = 1};
+ break;
+ case 933*MHz:
+ dpll_cfg = (struct pll_div)
+ {.refdiv = 1, .fbdiv = 116, .postdiv1 = 3, .postdiv2 = 1};
+ break;
+ default:
+ error("Unsupported SDRAM frequency!,%ld\n", set_rate);
+ }
+ rkclk_set_pll(&cru->dpll_con[0], &dpll_cfg);
+
+ return set_rate;
+}
static ulong rk3399_clk_get_rate(struct clk *clk)
{
struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
case SCLK_I2C7:
rate = rk3399_i2c_get_clk(priv->cru, clk->id);
break;
+ case SCLK_SPI0...SCLK_SPI5:
+ rate = rk3399_spi_get_clk(priv->cru, clk->id);
+ break;
+ case SCLK_UART0:
+ case SCLK_UART2:
+ return 24000000;
case DCLK_VOP0:
case DCLK_VOP1:
break;
case SCLK_EMMC:
ret = rk3399_mmc_set_clk(priv->cru, clk->id, rate);
break;
+ case SCLK_MAC:
+ /* nothing to do, as this is an external clock */
+ ret = rate;
+ break;
case SCLK_I2C1:
case SCLK_I2C2:
case SCLK_I2C3:
case SCLK_I2C7:
ret = rk3399_i2c_set_clk(priv->cru, clk->id, rate);
break;
+ case SCLK_SPI0...SCLK_SPI5:
+ ret = rk3399_spi_set_clk(priv->cru, clk->id, rate);
+ break;
case DCLK_VOP0:
case DCLK_VOP1:
ret = rk3399_vop_set_clk(priv->cru, clk->id, rate);
break;
+ case SCLK_DDRCLK:
+ ret = rk3399_ddr_set_clk(priv->cru, rate);
+ break;
default:
return -ENOENT;
}
static int rk3399_clk_probe(struct udevice *dev)
{
+#ifdef CONFIG_SPL_BUILD
struct rk3399_clk_priv *priv = dev_get_priv(dev);
- rkclk_init(priv->cru);
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct rk3399_clk_plat *plat = dev_get_platdata(dev);
+ priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]);
+#endif
+ rkclk_init(priv->cru);
+#endif
return 0;
}
static int rk3399_clk_ofdata_to_platdata(struct udevice *dev)
{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
struct rk3399_clk_priv *priv = dev_get_priv(dev);
priv->cru = (struct rk3399_cru *)dev_get_addr(dev);
-
+#endif
return 0;
}
};
U_BOOT_DRIVER(clk_rk3399) = {
- .name = "clk_rk3399",
+ .name = "rockchip_rk3399_cru",
.id = UCLASS_CLK,
.of_match = rk3399_clk_ids,
.priv_auto_alloc_size = sizeof(struct rk3399_clk_priv),
.ops = &rk3399_clk_ops,
.bind = rk3399_clk_bind,
.probe = rk3399_clk_probe,
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ .platdata_auto_alloc_size = sizeof(struct rk3399_clk_plat),
+#endif
};
static ulong rk3399_i2c_get_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id)
.set_rate = rk3399_pmuclk_set_rate,
};
+#ifndef CONFIG_SPL_BUILD
static void pmuclk_init(struct rk3399_pmucru *pmucru)
{
u32 pclk_div;
/* configure pmu pclk */
pclk_div = PPLL_HZ / PMU_PCLK_HZ - 1;
- assert((pclk_div + 1) * PMU_PCLK_HZ == PPLL_HZ && pclk_div < 0x1f);
rk_clrsetreg(&pmucru->pmucru_clksel[0],
PMU_PCLK_DIV_CON_MASK,
pclk_div << PMU_PCLK_DIV_CON_SHIFT);
}
+#endif
static int rk3399_pmuclk_probe(struct udevice *dev)
{
+#if CONFIG_IS_ENABLED(OF_PLATDATA) || !defined(CONFIG_SPL_BUILD)
struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
+#endif
- pmuclk_init(priv->pmucru);
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct rk3399_pmuclk_plat *plat = dev_get_platdata(dev);
+
+ priv->pmucru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]);
+#endif
+#ifndef CONFIG_SPL_BUILD
+ pmuclk_init(priv->pmucru);
+#endif
return 0;
}
static int rk3399_pmuclk_ofdata_to_platdata(struct udevice *dev)
{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
priv->pmucru = (struct rk3399_pmucru *)dev_get_addr(dev);
-
+#endif
return 0;
}
};
U_BOOT_DRIVER(rockchip_rk3399_pmuclk) = {
- .name = "pmuclk_rk3399",
+ .name = "rockchip_rk3399_pmucru",
.id = UCLASS_CLK,
.of_match = rk3399_pmuclk_ids,
.priv_auto_alloc_size = sizeof(struct rk3399_pmuclk_priv),
.ofdata_to_platdata = rk3399_pmuclk_ofdata_to_platdata,
.ops = &rk3399_pmuclk_ops,
.probe = rk3399_pmuclk_probe,
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ .platdata_auto_alloc_size = sizeof(struct rk3399_pmuclk_plat),
+#endif
};