// SPDX-License-Identifier: GPL-2.0 /* * (C) Copyright 2017-2019 Rockchip Electronics Co., Ltd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; enum { VCO_MAX_HZ = 3200U * 1000000, VCO_MIN_HZ = 800 * 1000000, OUTPUT_MAX_HZ = 3200U * 1000000, OUTPUT_MIN_HZ = 24 * 1000000, }; #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define RK3308_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \ { \ .rate = _rate##U, \ .aclk_div = _aclk_div, \ .pclk_div = _pclk_div, \ } static struct rockchip_pll_rate_table rk3308_pll_rates[] = { /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ RK3036_PLL_RATE(1300000000, 6, 325, 1, 1, 1, 0), RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), RK3036_PLL_RATE(748000000, 2, 187, 3, 1, 1, 0), }; static struct rockchip_cpu_rate_table rk3308_cpu_rates[] = { RK3308_CPUCLK_RATE(1200000000, 1, 5), RK3308_CPUCLK_RATE(1008000000, 1, 5), RK3308_CPUCLK_RATE(816000000, 1, 3), RK3308_CPUCLK_RATE(600000000, 1, 3), RK3308_CPUCLK_RATE(408000000, 1, 1), }; static struct rockchip_pll_clock rk3308_pll_clks[] = { [APLL] = PLL(pll_rk3328, PLL_APLL, RK3308_PLL_CON(0), RK3308_MODE_CON, 0, 10, 0, rk3308_pll_rates), [DPLL] = PLL(pll_rk3328, PLL_DPLL, RK3308_PLL_CON(8), RK3308_MODE_CON, 2, 10, 0, NULL), [VPLL0] = PLL(pll_rk3328, PLL_VPLL0, RK3308_PLL_CON(16), RK3308_MODE_CON, 4, 10, 0, NULL), [VPLL1] = PLL(pll_rk3328, PLL_VPLL1, RK3308_PLL_CON(24), RK3308_MODE_CON, 6, 10, 0, NULL), }; static ulong rk3308_armclk_set_clk(struct rk3308_clk_priv *priv, ulong hz) { struct rk3308_cru *cru = priv->cru; const struct rockchip_cpu_rate_table *rate; ulong old_rate; rate = rockchip_get_cpu_settings(rk3308_cpu_rates, hz); if (!rate) { printf("%s unsupport rate\n", __func__); return -EINVAL; } /* * select apll as cpu/core clock pll source and * set up dependent divisors for PERI and ACLK clocks. * core hz : apll = 1:1 */ old_rate = rockchip_pll_get_rate(&rk3308_pll_clks[APLL], priv->cru, APLL); if (old_rate > hz) { if (rockchip_pll_set_rate(&rk3308_pll_clks[APLL], priv->cru, APLL, hz)) return -EINVAL; rk_clrsetreg(&cru->clksel_con[0], CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, rate->aclk_div << CORE_ACLK_DIV_SHIFT | rate->pclk_div << CORE_DBG_DIV_SHIFT | CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | 0 << CORE_DIV_CON_SHIFT); } else if (old_rate < hz) { rk_clrsetreg(&cru->clksel_con[0], CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, rate->aclk_div << CORE_ACLK_DIV_SHIFT | rate->pclk_div << CORE_DBG_DIV_SHIFT | CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | 0 << CORE_DIV_CON_SHIFT); if (rockchip_pll_set_rate(&rk3308_pll_clks[APLL], priv->cru, APLL, hz)) return -EINVAL; } return rockchip_pll_get_rate(&rk3308_pll_clks[APLL], priv->cru, APLL); } static void rk3308_clk_get_pll_rate(struct rk3308_clk_priv *priv) { if (!priv->dpll_hz) priv->dpll_hz = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], priv->cru, DPLL); if (!priv->vpll0_hz) priv->vpll0_hz = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], priv->cru, VPLL0); if (!priv->vpll1_hz) priv->vpll1_hz = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], priv->cru, VPLL1); } static ulong rk3308_i2c_get_clk(struct clk *clk) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); struct rk3308_cru *cru = priv->cru; u32 div, con, con_id; switch (clk->id) { case SCLK_I2C0: con_id = 25; break; case SCLK_I2C1: con_id = 26; break; case SCLK_I2C2: con_id = 27; break; case SCLK_I2C3: con_id = 28; break; default: printf("do not support this i2c bus\n"); return -EINVAL; } con = readl(&cru->clksel_con[con_id]); div = con >> CLK_I2C_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; return DIV_TO_RATE(priv->dpll_hz, div); } static ulong rk3308_i2c_set_clk(struct clk *clk, uint hz) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); struct rk3308_cru *cru = priv->cru; u32 src_clk_div, con_id; src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); assert(src_clk_div - 1 <= 127); switch (clk->id) { case SCLK_I2C0: con_id = 25; break; case SCLK_I2C1: con_id = 26; break; case SCLK_I2C2: con_id = 27; break; case SCLK_I2C3: con_id = 28; break; default: printf("do not support this i2c bus\n"); return -EINVAL; } rk_clrsetreg(&cru->clksel_con[con_id], CLK_I2C_PLL_SEL_MASK | CLK_I2C_DIV_CON_MASK, CLK_I2C_PLL_SEL_DPLL << CLK_I2C_PLL_SEL_SHIFT | (src_clk_div - 1) << CLK_I2C_DIV_CON_SHIFT); return rk3308_i2c_get_clk(clk); } static ulong rk3308_mac_set_clk(struct clk *clk, uint hz) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); struct rk3308_cru *cru = priv->cru; u32 con = readl(&cru->clksel_con[43]); ulong pll_rate; u8 div; if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL0) pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], priv->cru, VPLL0); else if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL1) pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], priv->cru, VPLL1); else pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], priv->cru, DPLL); /*default set 50MHZ for gmac*/ if (!hz) hz = 50000000; div = DIV_ROUND_UP(pll_rate, hz) - 1; assert(div < 32); rk_clrsetreg(&cru->clksel_con[43], MAC_DIV_MASK, div << MAC_DIV_SHIFT); return DIV_TO_RATE(pll_rate, div); } static int rk3308_mac_set_speed_clk(struct clk *clk, uint hz) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); struct rk3308_cru *cru = priv->cru; if (hz != 2500000 && hz != 25000000) { debug("Unsupported mac speed:%d\n", hz); return -EINVAL; } rk_clrsetreg(&cru->clksel_con[43], MAC_CLK_SPEED_SEL_MASK, ((hz == 2500000) ? 0 : 1) << MAC_CLK_SPEED_SEL_SHIFT); return 0; } static ulong rk3308_mmc_get_clk(struct clk *clk) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); struct rk3308_cru *cru = priv->cru;
#-------------------------------------------------
## Graphics section ##
#-------------------------------------------------
PACKAGES_GFX_${MACHINE} = "ti-sgx-ddk-km"

# Enable Gfx Pkgs
MACHINE_FEATURES_append = " sgx"
MULTI_PROVIDER_WHITELIST += "virtual/libgl virtual/egl virtual/libgles1 virtual/libgles2"

DEPENDS_remove = "virtual/libgl"

# Preferred providers
PREFERRED_PROVIDER_virtual/libgles1 = ""
PREFERRED_PROVIDER_virtual/libgles2 = "ti-sgx-ddk-um"
PREFERRED_PROVIDER_virtual/egl      = "ti-sgx-ddk-um"
PREFERRED_PROVIDER_virtual/libgbm   = "ti-sgx-ddk-um"
PREFERRED_PROVIDER_virtual/mesa     = "mesa-gl"

#-------------------------------------------------
## Multimedia section ##
#-------------------------------------------------

PACKAGES_MULTIMEDIA   = " \
    ipumm-fw \
    ${@bb.utils.contains('DISTRO_FEATURES', 'pulseaudio', 'pulseaudio-misc' , '', d)}      \
"

PREFERRED_PROVIDER_virtual/kernel  = "linux-ti-staging"
PREFERRED_VERSION_virtual/kernel  = "4.9"

# Distribution-specific runtime components
IMAGE_INSTALL_append = " \
     ${PACKAGES_GFX}     \
     ${PACKAGES_MULTIMEDIA}     \
     cmem \
"

# Include WIC support based on beaglebone
do_image_wic[depends] += "mtools-native:do_populate_sysroot dosfstools-native:do_populate_sysroot"
WKS_FILE = "sdimage-bootpart-uuid.wks"
IMAGE_BOOT_FILES = "MLO u-boot.img uEnv.txt"
_pll_clks[APLL], priv->cru, APLL); break; case PLL_DPLL: rate = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], priv->cru, DPLL); break; case PLL_VPLL0: rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], priv->cru, VPLL0); break; case PLL_VPLL1: rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], priv->cru, VPLL1); break; case HCLK_SDMMC: case HCLK_EMMC: case SCLK_SDMMC: case SCLK_EMMC: case SCLK_EMMC_SAMPLE: rate = rk3308_mmc_get_clk(clk); break; case SCLK_I2C0: case SCLK_I2C1: case SCLK_I2C2: case SCLK_I2C3: rate = rk3308_i2c_get_clk(clk); break; case SCLK_SARADC: rate = rk3308_saradc_get_clk(clk); break; case SCLK_TSADC: rate = rk3308_tsadc_get_clk(clk); break; case SCLK_SPI0: case SCLK_SPI1: rate = rk3308_spi_get_clk(clk); break; case SCLK_PWM0: rate = rk3308_pwm_get_clk(clk); break; case DCLK_VOP: rate = rk3308_vop_get_clk(clk); break; case ACLK_BUS: case HCLK_BUS: case PCLK_BUS: case PCLK_WDT: rate = rk3308_bus_get_clk(priv, clk->id); break; case ACLK_PERI: case HCLK_PERI: case PCLK_PERI: rate = rk3308_peri_get_clk(priv, clk->id); break; case HCLK_AUDIO: case PCLK_AUDIO: rate = rk3308_audio_get_clk(priv, clk->id); break; case SCLK_CRYPTO: case SCLK_CRYPTO_APK: rate = rk3308_crypto_get_clk(priv, clk->id); break; default: return -ENOENT; } return rate; } static ulong rk3308_clk_set_rate(struct clk *clk, ulong rate) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); ulong ret = 0; debug("%s %ld %ld\n", __func__, clk->id, rate); switch (clk->id) { case PLL_DPLL: ret = rockchip_pll_set_rate(&rk3308_pll_clks[DPLL], priv->cru, DPLL, rate); priv->dpll_hz = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], priv->cru, DPLL); break; case ARMCLK: if (priv->armclk_hz) rk3308_armclk_set_clk(priv, rate); priv->armclk_hz = rate; break; case HCLK_SDMMC: case HCLK_EMMC: case SCLK_SDMMC: case SCLK_EMMC: ret = rk3308_mmc_set_clk(clk, rate); break; case SCLK_I2C0: case SCLK_I2C1: case SCLK_I2C2: case SCLK_I2C3: ret = rk3308_i2c_set_clk(clk, rate); break; case SCLK_MAC: ret = rk3308_mac_set_clk(clk, rate); break; case SCLK_MAC_RMII: ret = rk3308_mac_set_speed_clk(clk, rate); break; case SCLK_SARADC: ret = rk3308_saradc_set_clk(clk, rate); break; case SCLK_TSADC: ret = rk3308_tsadc_set_clk(clk, rate); break; case SCLK_SPI0: case SCLK_SPI1: ret = rk3308_spi_set_clk(clk, rate); break; case SCLK_PWM0: ret = rk3308_pwm_set_clk(clk, rate); break; case DCLK_VOP: ret = rk3308_vop_set_clk(clk, rate); break; case ACLK_BUS: case HCLK_BUS: case PCLK_BUS: rate = rk3308_bus_set_clk(priv, clk->id, rate); break; case ACLK_PERI: case HCLK_PERI: case PCLK_PERI: rate = rk3308_peri_set_clk(priv, clk->id, rate); break; case HCLK_AUDIO: case PCLK_AUDIO: rate = rk3308_audio_set_clk(priv, clk->id, rate); break; case SCLK_CRYPTO: case SCLK_CRYPTO_APK: ret = rk3308_crypto_set_clk(priv, clk->id, rate); break; default: return -ENOENT; } return ret; } #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) static int __maybe_unused rk3308_mac_set_parent(struct clk *clk, struct clk *parent) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); /* * If the requested parent is in the same clock-controller and * the id is SCLK_MAC_SRC, switch to the internal clock. */ if (parent->id == SCLK_MAC_SRC) { debug("%s: switching RMII to SCLK_MAC\n", __func__); rk_clrreg(&priv->cru->clksel_con[43], BIT(14)); } else { debug("%s: switching RMII to CLKIN\n", __func__); rk_setreg(&priv->cru->clksel_con[43], BIT(14)); } return 0; } static int __maybe_unused rk3308_clk_set_parent(struct clk *clk, struct clk *parent) { switch (clk->id) { case SCLK_MAC: return rk3308_mac_set_parent(clk, parent); default: break; } debug("%s: unsupported clk %ld\n", __func__, clk->id); return -ENOENT; } #endif static struct clk_ops rk3308_clk_ops = { .get_rate = rk3308_clk_get_rate, .set_rate = rk3308_clk_set_rate, #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) .set_parent = rk3308_clk_set_parent, #endif }; static void rk3308_clk_init(struct udevice *dev) { struct rk3308_clk_priv *priv = dev_get_priv(dev); int ret; if (rockchip_pll_get_rate(&rk3308_pll_clks[APLL], priv->cru, APLL) != APLL_HZ) { ret = rk3308_armclk_set_clk(priv, APLL_HZ); if (ret < 0) printf("%s failed to set armclk rate\n", __func__); } rk3308_clk_get_pll_rate(priv); rk3308_bus_set_clk(priv, ACLK_BUS, BUS_ACLK_HZ); rk3308_bus_set_clk(priv, HCLK_BUS, BUS_HCLK_HZ); rk3308_bus_set_clk(priv, PCLK_BUS, BUS_PCLK_HZ); rk3308_peri_set_clk(priv, ACLK_PERI, PERI_ACLK_HZ); rk3308_peri_set_clk(priv, HCLK_PERI, PERI_HCLK_HZ); rk3308_peri_set_clk(priv, PCLK_PERI, PERI_PCLK_HZ); rk3308_audio_set_clk(priv, HCLK_AUDIO, AUDIO_HCLK_HZ); rk3308_audio_set_clk(priv, PCLK_AUDIO, AUDIO_PCLK_HZ); } static int rk3308_clk_probe(struct udevice *dev) { int ret; rk3308_clk_init(dev); /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ ret = clk_set_defaults(dev, 1); if (ret) debug("%s clk_set_defaults failed %d\n", __func__, ret); return ret; } static int rk3308_clk_of_to_plat(struct udevice *dev) { struct rk3308_clk_priv *priv = dev_get_priv(dev); priv->cru = dev_read_addr_ptr(dev); return 0; } static int rk3308_clk_bind(struct udevice *dev) { int ret; struct udevice *sys_child; struct sysreset_reg *priv; /* The reset driver does not have a device node, so bind it here */ ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", &sys_child); if (ret) { debug("Warning: No sysreset driver: ret=%d\n", ret); } else { priv = malloc(sizeof(struct sysreset_reg)); priv->glb_srst_fst_value = offsetof(struct rk3308_cru, glb_srst_fst); priv->glb_srst_snd_value = offsetof(struct rk3308_cru, glb_srst_snd); dev_set_priv(sys_child, priv); } #if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3308_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 12); if (ret) debug("Warning: software reset driver bind faile\n"); #endif return 0; } static const struct udevice_id rk3308_clk_ids[] = { { .compatible = "rockchip,rk3308-cru" }, { } }; U_BOOT_DRIVER(rockchip_rk3308_cru) = { .name = "rockchip_rk3308_cru", .id = UCLASS_CLK, .of_match = rk3308_clk_ids, .priv_auto = sizeof(struct rk3308_clk_priv), .of_to_plat = rk3308_clk_of_to_plat, .ops = &rk3308_clk_ops, .bind = rk3308_clk_bind, .probe = rk3308_clk_probe, };