1dcdd3278SHeiko Stübner /* 2dcdd3278SHeiko Stübner * (C) Copyright 2015 Google, Inc 3dcdd3278SHeiko Stübner * (C) Copyright 2016 Heiko Stuebner <heiko@sntech.de> 4dcdd3278SHeiko Stübner * 5dcdd3278SHeiko Stübner * SPDX-License-Identifier: GPL-2.0 6dcdd3278SHeiko Stübner */ 7dcdd3278SHeiko Stübner 8dcdd3278SHeiko Stübner #include <common.h> 9dcdd3278SHeiko Stübner #include <clk-uclass.h> 10dcdd3278SHeiko Stübner #include <dm.h> 11dcdd3278SHeiko Stübner #include <dt-structs.h> 12dcdd3278SHeiko Stübner #include <errno.h> 13dcdd3278SHeiko Stübner #include <mapmem.h> 14dcdd3278SHeiko Stübner #include <syscon.h> 15dcdd3278SHeiko Stübner #include <asm/io.h> 16dcdd3278SHeiko Stübner #include <asm/arch/clock.h> 17dcdd3278SHeiko Stübner #include <asm/arch/cru_rk3188.h> 18dcdd3278SHeiko Stübner #include <asm/arch/grf_rk3188.h> 19dcdd3278SHeiko Stübner #include <asm/arch/hardware.h> 20dcdd3278SHeiko Stübner #include <dt-bindings/clock/rk3188-cru.h> 21dcdd3278SHeiko Stübner #include <dm/device-internal.h> 22dcdd3278SHeiko Stübner #include <dm/lists.h> 23dcdd3278SHeiko Stübner #include <dm/uclass-internal.h> 24dcdd3278SHeiko Stübner #include <linux/log2.h> 25dcdd3278SHeiko Stübner 26dcdd3278SHeiko Stübner DECLARE_GLOBAL_DATA_PTR; 27dcdd3278SHeiko Stübner 28dcdd3278SHeiko Stübner enum rk3188_clk_type { 29dcdd3278SHeiko Stübner RK3188_CRU, 30dcdd3278SHeiko Stübner RK3188A_CRU, 31dcdd3278SHeiko Stübner }; 32dcdd3278SHeiko Stübner 33dcdd3278SHeiko Stübner struct rk3188_clk_plat { 34dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA) 35dcdd3278SHeiko Stübner struct dtd_rockchip_rk3188_cru dtd; 36dcdd3278SHeiko Stübner #endif 37dcdd3278SHeiko Stübner }; 38dcdd3278SHeiko Stübner 39dcdd3278SHeiko Stübner struct pll_div { 40dcdd3278SHeiko Stübner u32 nr; 41dcdd3278SHeiko Stübner u32 nf; 42dcdd3278SHeiko Stübner u32 no; 43dcdd3278SHeiko Stübner }; 44dcdd3278SHeiko Stübner 45dcdd3278SHeiko Stübner enum { 46dcdd3278SHeiko Stübner VCO_MAX_HZ = 2200U * 1000000, 47dcdd3278SHeiko Stübner VCO_MIN_HZ = 440 * 1000000, 48dcdd3278SHeiko Stübner OUTPUT_MAX_HZ = 2200U * 1000000, 49dcdd3278SHeiko Stübner OUTPUT_MIN_HZ = 30 * 1000000, 50dcdd3278SHeiko Stübner FREF_MAX_HZ = 2200U * 1000000, 51dcdd3278SHeiko Stübner FREF_MIN_HZ = 30 * 1000, 52dcdd3278SHeiko Stübner }; 53dcdd3278SHeiko Stübner 54dcdd3278SHeiko Stübner enum { 55dcdd3278SHeiko Stübner /* PLL CON0 */ 56dcdd3278SHeiko Stübner PLL_OD_MASK = 0x0f, 57dcdd3278SHeiko Stübner 58dcdd3278SHeiko Stübner /* PLL CON1 */ 59dcdd3278SHeiko Stübner PLL_NF_MASK = 0x1fff, 60dcdd3278SHeiko Stübner 61dcdd3278SHeiko Stübner /* PLL CON2 */ 62dcdd3278SHeiko Stübner PLL_BWADJ_MASK = 0x0fff, 63dcdd3278SHeiko Stübner 64dcdd3278SHeiko Stübner /* PLL CON3 */ 65dcdd3278SHeiko Stübner PLL_RESET_SHIFT = 5, 66dcdd3278SHeiko Stübner 67dcdd3278SHeiko Stübner /* GRF_SOC_STATUS0 */ 68dcdd3278SHeiko Stübner SOCSTS_DPLL_LOCK = 1 << 5, 69dcdd3278SHeiko Stübner SOCSTS_APLL_LOCK = 1 << 6, 70dcdd3278SHeiko Stübner SOCSTS_CPLL_LOCK = 1 << 7, 71dcdd3278SHeiko Stübner SOCSTS_GPLL_LOCK = 1 << 8, 72dcdd3278SHeiko Stübner }; 73dcdd3278SHeiko Stübner 74dcdd3278SHeiko Stübner #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) 75dcdd3278SHeiko Stübner 76dcdd3278SHeiko Stübner #define PLL_DIVISORS(hz, _nr, _no) {\ 77dcdd3278SHeiko Stübner .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\ 78dcdd3278SHeiko Stübner _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\ 79dcdd3278SHeiko Stübner (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\ 80dcdd3278SHeiko Stübner "divisors on line " __stringify(__LINE__)); 81dcdd3278SHeiko Stübner 82dcdd3278SHeiko Stübner /* Keep divisors as low as possible to reduce jitter and power usage */ 83dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 84dcdd3278SHeiko Stübner static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2); 85dcdd3278SHeiko Stübner static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2); 86dcdd3278SHeiko Stübner #endif 87dcdd3278SHeiko Stübner 88dcdd3278SHeiko Stübner static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id, 89dcdd3278SHeiko Stübner const struct pll_div *div, bool has_bwadj) 90dcdd3278SHeiko Stübner { 91dcdd3278SHeiko Stübner int pll_id = rk_pll_id(clk_id); 92dcdd3278SHeiko Stübner struct rk3188_pll *pll = &cru->pll[pll_id]; 93dcdd3278SHeiko Stübner /* All PLLs have same VCO and output frequency range restrictions. */ 94dcdd3278SHeiko Stübner uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000; 95dcdd3278SHeiko Stübner uint output_hz = vco_hz / div->no; 96dcdd3278SHeiko Stübner 97dcdd3278SHeiko Stübner debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", 98dcdd3278SHeiko Stübner (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz); 99dcdd3278SHeiko Stübner assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && 100dcdd3278SHeiko Stübner output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ && 101dcdd3278SHeiko Stübner (div->no == 1 || !(div->no % 2))); 102dcdd3278SHeiko Stübner 103dcdd3278SHeiko Stübner /* enter reset */ 104dcdd3278SHeiko Stübner rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT); 105dcdd3278SHeiko Stübner 106dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con0, 107dcdd3278SHeiko Stübner CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK, 108dcdd3278SHeiko Stübner ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1)); 109dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1); 110dcdd3278SHeiko Stübner 111dcdd3278SHeiko Stübner if (has_bwadj) 112dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); 113dcdd3278SHeiko Stübner 114dcdd3278SHeiko Stübner udelay(10); 115dcdd3278SHeiko Stübner 116dcdd3278SHeiko Stübner /* return from reset */ 117dcdd3278SHeiko Stübner rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT); 118dcdd3278SHeiko Stübner 119dcdd3278SHeiko Stübner return 0; 120dcdd3278SHeiko Stübner } 121dcdd3278SHeiko Stübner 122dcdd3278SHeiko Stübner static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf, 123dcdd3278SHeiko Stübner unsigned int hz, bool has_bwadj) 124dcdd3278SHeiko Stübner { 125dcdd3278SHeiko Stübner static const struct pll_div dpll_cfg[] = { 126dcdd3278SHeiko Stübner {.nf = 25, .nr = 2, .no = 1}, 127dcdd3278SHeiko Stübner {.nf = 400, .nr = 9, .no = 2}, 128dcdd3278SHeiko Stübner {.nf = 500, .nr = 9, .no = 2}, 129dcdd3278SHeiko Stübner {.nf = 100, .nr = 3, .no = 1}, 130dcdd3278SHeiko Stübner }; 131dcdd3278SHeiko Stübner int cfg; 132dcdd3278SHeiko Stübner 133dcdd3278SHeiko Stübner switch (hz) { 134dcdd3278SHeiko Stübner case 300000000: 135dcdd3278SHeiko Stübner cfg = 0; 136dcdd3278SHeiko Stübner break; 137dcdd3278SHeiko Stübner case 533000000: /* actually 533.3P MHz */ 138dcdd3278SHeiko Stübner cfg = 1; 139dcdd3278SHeiko Stübner break; 140dcdd3278SHeiko Stübner case 666000000: /* actually 666.6P MHz */ 141dcdd3278SHeiko Stübner cfg = 2; 142dcdd3278SHeiko Stübner break; 143dcdd3278SHeiko Stübner case 800000000: 144dcdd3278SHeiko Stübner cfg = 3; 145dcdd3278SHeiko Stübner break; 146dcdd3278SHeiko Stübner default: 147dcdd3278SHeiko Stübner debug("Unsupported SDRAM frequency"); 148dcdd3278SHeiko Stübner return -EINVAL; 149dcdd3278SHeiko Stübner } 150dcdd3278SHeiko Stübner 151dcdd3278SHeiko Stübner /* pll enter slow-mode */ 152dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, 153dcdd3278SHeiko Stübner DPLL_MODE_SLOW << DPLL_MODE_SHIFT); 154dcdd3278SHeiko Stübner 155dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj); 156dcdd3278SHeiko Stübner 157dcdd3278SHeiko Stübner /* wait for pll lock */ 158dcdd3278SHeiko Stübner while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK)) 159dcdd3278SHeiko Stübner udelay(1); 160dcdd3278SHeiko Stübner 161dcdd3278SHeiko Stübner /* PLL enter normal-mode */ 162dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, 163dcdd3278SHeiko Stübner DPLL_MODE_NORMAL << DPLL_MODE_SHIFT); 164dcdd3278SHeiko Stübner 165dcdd3278SHeiko Stübner return 0; 166dcdd3278SHeiko Stübner } 167dcdd3278SHeiko Stübner 168f7853570SHeiko Stübner static int rkclk_configure_cpu(struct rk3188_cru *cru, struct rk3188_grf *grf, 169f7853570SHeiko Stübner unsigned int hz, bool has_bwadj) 170f7853570SHeiko Stübner { 171f7853570SHeiko Stübner static const struct pll_div apll_cfg[] = { 172f7853570SHeiko Stübner {.nf = 50, .nr = 1, .no = 2}, 173f7853570SHeiko Stübner {.nf = 67, .nr = 1, .no = 1}, 174f7853570SHeiko Stübner }; 175f7853570SHeiko Stübner int div_core_peri, div_aclk_core, cfg; 176f7853570SHeiko Stübner 177f7853570SHeiko Stübner /* 178f7853570SHeiko Stübner * We support two possible frequencies, the safe 600MHz 179f7853570SHeiko Stübner * which will work with default pmic settings and will 180f7853570SHeiko Stübner * be set in SPL to get away from the 24MHz default and 181f7853570SHeiko Stübner * the maximum of 1.6Ghz, which boards can set if they 182f7853570SHeiko Stübner * were able to get pmic support for it. 183f7853570SHeiko Stübner */ 184f7853570SHeiko Stübner switch (hz) { 185f7853570SHeiko Stübner case APLL_SAFE_HZ: 186f7853570SHeiko Stübner cfg = 0; 187f7853570SHeiko Stübner div_core_peri = 1; 188f7853570SHeiko Stübner div_aclk_core = 3; 189f7853570SHeiko Stübner break; 190f7853570SHeiko Stübner case APLL_HZ: 191f7853570SHeiko Stübner cfg = 1; 192f7853570SHeiko Stübner div_core_peri = 2; 193f7853570SHeiko Stübner div_aclk_core = 3; 194f7853570SHeiko Stübner break; 195f7853570SHeiko Stübner default: 196f7853570SHeiko Stübner debug("Unsupported ARMCLK frequency"); 197f7853570SHeiko Stübner return -EINVAL; 198f7853570SHeiko Stübner } 199f7853570SHeiko Stübner 200f7853570SHeiko Stübner /* pll enter slow-mode */ 201f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT, 202f7853570SHeiko Stübner APLL_MODE_SLOW << APLL_MODE_SHIFT); 203f7853570SHeiko Stübner 204f7853570SHeiko Stübner rkclk_set_pll(cru, CLK_ARM, &apll_cfg[cfg], has_bwadj); 205f7853570SHeiko Stübner 206f7853570SHeiko Stübner /* waiting for pll lock */ 207f7853570SHeiko Stübner while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK)) 208f7853570SHeiko Stübner udelay(1); 209f7853570SHeiko Stübner 210f7853570SHeiko Stübner /* Set divider for peripherals attached to the cpu core. */ 211f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[0], 212f7853570SHeiko Stübner CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT, 213f7853570SHeiko Stübner div_core_peri << CORE_PERI_DIV_SHIFT); 214f7853570SHeiko Stübner 215f7853570SHeiko Stübner /* set up dependent divisor for aclk_core */ 216f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[1], 217f7853570SHeiko Stübner CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT, 218f7853570SHeiko Stübner div_aclk_core << CORE_ACLK_DIV_SHIFT); 219f7853570SHeiko Stübner 220f7853570SHeiko Stübner /* PLL enter normal-mode */ 221f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT, 222f7853570SHeiko Stübner APLL_MODE_NORMAL << APLL_MODE_SHIFT); 223f7853570SHeiko Stübner 224f7853570SHeiko Stübner return hz; 225f7853570SHeiko Stübner } 226f7853570SHeiko Stübner 227dcdd3278SHeiko Stübner /* Get pll rate by id */ 228dcdd3278SHeiko Stübner static uint32_t rkclk_pll_get_rate(struct rk3188_cru *cru, 229dcdd3278SHeiko Stübner enum rk_clk_id clk_id) 230dcdd3278SHeiko Stübner { 231dcdd3278SHeiko Stübner uint32_t nr, no, nf; 232dcdd3278SHeiko Stübner uint32_t con; 233dcdd3278SHeiko Stübner int pll_id = rk_pll_id(clk_id); 234dcdd3278SHeiko Stübner struct rk3188_pll *pll = &cru->pll[pll_id]; 235dcdd3278SHeiko Stübner static u8 clk_shift[CLK_COUNT] = { 236dcdd3278SHeiko Stübner 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, 237dcdd3278SHeiko Stübner GPLL_MODE_SHIFT 238dcdd3278SHeiko Stübner }; 239dcdd3278SHeiko Stübner uint shift; 240dcdd3278SHeiko Stübner 241dcdd3278SHeiko Stübner con = readl(&cru->cru_mode_con); 242dcdd3278SHeiko Stübner shift = clk_shift[clk_id]; 243dcdd3278SHeiko Stübner switch ((con >> shift) & APLL_MODE_MASK) { 244dcdd3278SHeiko Stübner case APLL_MODE_SLOW: 245dcdd3278SHeiko Stübner return OSC_HZ; 246dcdd3278SHeiko Stübner case APLL_MODE_NORMAL: 247dcdd3278SHeiko Stübner /* normal mode */ 248dcdd3278SHeiko Stübner con = readl(&pll->con0); 249dcdd3278SHeiko Stübner no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; 250dcdd3278SHeiko Stübner nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1; 251dcdd3278SHeiko Stübner con = readl(&pll->con1); 252dcdd3278SHeiko Stübner nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; 253dcdd3278SHeiko Stübner 254dcdd3278SHeiko Stübner return (24 * nf / (nr * no)) * 1000000; 255dcdd3278SHeiko Stübner case APLL_MODE_DEEP: 256dcdd3278SHeiko Stübner default: 257dcdd3278SHeiko Stübner return 32768; 258dcdd3278SHeiko Stübner } 259dcdd3278SHeiko Stübner } 260dcdd3278SHeiko Stübner 261dcdd3278SHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate, 262dcdd3278SHeiko Stübner int periph) 263dcdd3278SHeiko Stübner { 264dcdd3278SHeiko Stübner uint div; 265dcdd3278SHeiko Stübner u32 con; 266dcdd3278SHeiko Stübner 267dcdd3278SHeiko Stübner switch (periph) { 268dcdd3278SHeiko Stübner case HCLK_EMMC: 2697a25a63cSXu Ziyuan case SCLK_EMMC: 270dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[12]); 271dcdd3278SHeiko Stübner div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; 272dcdd3278SHeiko Stübner break; 273dcdd3278SHeiko Stübner case HCLK_SDMMC: 2747a25a63cSXu Ziyuan case SCLK_SDMMC: 275dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[11]); 276dcdd3278SHeiko Stübner div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; 277dcdd3278SHeiko Stübner break; 278dcdd3278SHeiko Stübner case HCLK_SDIO: 2797a25a63cSXu Ziyuan case SCLK_SDIO: 280dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[12]); 281dcdd3278SHeiko Stübner div = (con >> SDIO_DIV_SHIFT) & SDIO_DIV_MASK; 282dcdd3278SHeiko Stübner break; 283dcdd3278SHeiko Stübner default: 284dcdd3278SHeiko Stübner return -EINVAL; 285dcdd3278SHeiko Stübner } 286dcdd3278SHeiko Stübner 2873a94d75dSKever Yang return DIV_TO_RATE(gclk_rate, div) / 2; 288dcdd3278SHeiko Stübner } 289dcdd3278SHeiko Stübner 290dcdd3278SHeiko Stübner static ulong rockchip_mmc_set_clk(struct rk3188_cru *cru, uint gclk_rate, 291dcdd3278SHeiko Stübner int periph, uint freq) 292dcdd3278SHeiko Stübner { 293dcdd3278SHeiko Stübner int src_clk_div; 294dcdd3278SHeiko Stübner 295dcdd3278SHeiko Stübner debug("%s: gclk_rate=%u\n", __func__, gclk_rate); 2963a94d75dSKever Yang /* mmc clock defaulg div 2 internal, need provide double in cru */ 297217273cdSKever Yang src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq) - 1; 298dcdd3278SHeiko Stübner assert(src_clk_div <= 0x3f); 299dcdd3278SHeiko Stübner 300dcdd3278SHeiko Stübner switch (periph) { 301dcdd3278SHeiko Stübner case HCLK_EMMC: 3027a25a63cSXu Ziyuan case SCLK_EMMC: 303dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12], 304dcdd3278SHeiko Stübner EMMC_DIV_MASK << EMMC_DIV_SHIFT, 305dcdd3278SHeiko Stübner src_clk_div << EMMC_DIV_SHIFT); 306dcdd3278SHeiko Stübner break; 307dcdd3278SHeiko Stübner case HCLK_SDMMC: 3087a25a63cSXu Ziyuan case SCLK_SDMMC: 309dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[11], 310dcdd3278SHeiko Stübner MMC0_DIV_MASK << MMC0_DIV_SHIFT, 311dcdd3278SHeiko Stübner src_clk_div << MMC0_DIV_SHIFT); 312dcdd3278SHeiko Stübner break; 313dcdd3278SHeiko Stübner case HCLK_SDIO: 3147a25a63cSXu Ziyuan case SCLK_SDIO: 315dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12], 316dcdd3278SHeiko Stübner SDIO_DIV_MASK << SDIO_DIV_SHIFT, 317dcdd3278SHeiko Stübner src_clk_div << SDIO_DIV_SHIFT); 318dcdd3278SHeiko Stübner break; 319dcdd3278SHeiko Stübner default: 320dcdd3278SHeiko Stübner return -EINVAL; 321dcdd3278SHeiko Stübner } 322dcdd3278SHeiko Stübner 323dcdd3278SHeiko Stübner return rockchip_mmc_get_clk(cru, gclk_rate, periph); 324dcdd3278SHeiko Stübner } 325dcdd3278SHeiko Stübner 326dcdd3278SHeiko Stübner static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate, 327dcdd3278SHeiko Stübner int periph) 328dcdd3278SHeiko Stübner { 329dcdd3278SHeiko Stübner uint div; 330dcdd3278SHeiko Stübner u32 con; 331dcdd3278SHeiko Stübner 332dcdd3278SHeiko Stübner switch (periph) { 333dcdd3278SHeiko Stübner case SCLK_SPI0: 334dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[25]); 335dcdd3278SHeiko Stübner div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK; 336dcdd3278SHeiko Stübner break; 337dcdd3278SHeiko Stübner case SCLK_SPI1: 338dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[25]); 339dcdd3278SHeiko Stübner div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK; 340dcdd3278SHeiko Stübner break; 341dcdd3278SHeiko Stübner default: 342dcdd3278SHeiko Stübner return -EINVAL; 343dcdd3278SHeiko Stübner } 344dcdd3278SHeiko Stübner 345dcdd3278SHeiko Stübner return DIV_TO_RATE(gclk_rate, div); 346dcdd3278SHeiko Stübner } 347dcdd3278SHeiko Stübner 348dcdd3278SHeiko Stübner static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate, 349dcdd3278SHeiko Stübner int periph, uint freq) 350dcdd3278SHeiko Stübner { 351217273cdSKever Yang int src_clk_div = DIV_ROUND_UP(gclk_rate, freq) - 1; 352dcdd3278SHeiko Stübner 353217273cdSKever Yang assert(src_clk_div < 128); 354dcdd3278SHeiko Stübner switch (periph) { 355dcdd3278SHeiko Stübner case SCLK_SPI0: 356dcdd3278SHeiko Stübner assert(src_clk_div <= SPI0_DIV_MASK); 357dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25], 358dcdd3278SHeiko Stübner SPI0_DIV_MASK << SPI0_DIV_SHIFT, 359dcdd3278SHeiko Stübner src_clk_div << SPI0_DIV_SHIFT); 360dcdd3278SHeiko Stübner break; 361dcdd3278SHeiko Stübner case SCLK_SPI1: 362dcdd3278SHeiko Stübner assert(src_clk_div <= SPI1_DIV_MASK); 363dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25], 364dcdd3278SHeiko Stübner SPI1_DIV_MASK << SPI1_DIV_SHIFT, 365dcdd3278SHeiko Stübner src_clk_div << SPI1_DIV_SHIFT); 366dcdd3278SHeiko Stübner break; 367dcdd3278SHeiko Stübner default: 368dcdd3278SHeiko Stübner return -EINVAL; 369dcdd3278SHeiko Stübner } 370dcdd3278SHeiko Stübner 371dcdd3278SHeiko Stübner return rockchip_spi_get_clk(cru, gclk_rate, periph); 372dcdd3278SHeiko Stübner } 373dcdd3278SHeiko Stübner 374dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 375dcdd3278SHeiko Stübner static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf, 376dcdd3278SHeiko Stübner bool has_bwadj) 377dcdd3278SHeiko Stübner { 378dcdd3278SHeiko Stübner u32 aclk_div, hclk_div, pclk_div, h2p_div; 379dcdd3278SHeiko Stübner 380dcdd3278SHeiko Stübner /* pll enter slow-mode */ 381dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, 382dcdd3278SHeiko Stübner GPLL_MODE_MASK << GPLL_MODE_SHIFT | 383dcdd3278SHeiko Stübner CPLL_MODE_MASK << CPLL_MODE_SHIFT, 384dcdd3278SHeiko Stübner GPLL_MODE_SLOW << GPLL_MODE_SHIFT | 385dcdd3278SHeiko Stübner CPLL_MODE_SLOW << CPLL_MODE_SHIFT); 386dcdd3278SHeiko Stübner 387dcdd3278SHeiko Stübner /* init pll */ 388dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj); 389dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj); 390dcdd3278SHeiko Stübner 391dcdd3278SHeiko Stübner /* waiting for pll lock */ 392dcdd3278SHeiko Stübner while ((readl(&grf->soc_status0) & 393dcdd3278SHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) != 394dcdd3278SHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) 395dcdd3278SHeiko Stübner udelay(1); 396dcdd3278SHeiko Stübner 397dcdd3278SHeiko Stübner /* 398dcdd3278SHeiko Stübner * cpu clock pll source selection and 399dcdd3278SHeiko Stübner * reparent aclk_cpu_pre from apll to gpll 400dcdd3278SHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks. 401dcdd3278SHeiko Stübner */ 402217273cdSKever Yang aclk_div = DIV_ROUND_UP(GPLL_HZ, CPU_ACLK_HZ) - 1; 403217273cdSKever Yang assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f); 404dcdd3278SHeiko Stübner 405dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[0], 406dcdd3278SHeiko Stübner CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT | 407dcdd3278SHeiko Stübner A9_CPU_DIV_MASK << A9_CPU_DIV_SHIFT, 408dcdd3278SHeiko Stübner CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT | 409dcdd3278SHeiko Stübner aclk_div << A9_CPU_DIV_SHIFT); 410dcdd3278SHeiko Stübner 411dcdd3278SHeiko Stübner hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ); 412dcdd3278SHeiko Stübner assert((1 << hclk_div) * CPU_HCLK_HZ == CPU_ACLK_HZ && hclk_div < 0x3); 413dcdd3278SHeiko Stübner pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ); 414dcdd3278SHeiko Stübner assert((1 << pclk_div) * CPU_PCLK_HZ == CPU_ACLK_HZ && pclk_div < 0x4); 415dcdd3278SHeiko Stübner h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ); 416dcdd3278SHeiko Stübner assert((1 << h2p_div) * CPU_H2P_HZ == CPU_HCLK_HZ && pclk_div < 0x3); 417dcdd3278SHeiko Stübner 418dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[1], 419dcdd3278SHeiko Stübner AHB2APB_DIV_MASK << AHB2APB_DIV_SHIFT | 420dcdd3278SHeiko Stübner CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT | 421dcdd3278SHeiko Stübner CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT, 422dcdd3278SHeiko Stübner h2p_div << AHB2APB_DIV_SHIFT | 423dcdd3278SHeiko Stübner pclk_div << CPU_PCLK_DIV_SHIFT | 424dcdd3278SHeiko Stübner hclk_div << CPU_HCLK_DIV_SHIFT); 425dcdd3278SHeiko Stübner 426dcdd3278SHeiko Stübner /* 427dcdd3278SHeiko Stübner * peri clock pll source selection and 428dcdd3278SHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks. 429dcdd3278SHeiko Stübner */ 430dcdd3278SHeiko Stübner aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; 431dcdd3278SHeiko Stübner assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); 432dcdd3278SHeiko Stübner 433dcdd3278SHeiko Stübner hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ); 434dcdd3278SHeiko Stübner assert((1 << hclk_div) * PERI_HCLK_HZ == 435dcdd3278SHeiko Stübner PERI_ACLK_HZ && (hclk_div < 0x4)); 436dcdd3278SHeiko Stübner 437dcdd3278SHeiko Stübner pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ); 438dcdd3278SHeiko Stübner assert((1 << pclk_div) * PERI_PCLK_HZ == 439dcdd3278SHeiko Stübner PERI_ACLK_HZ && (pclk_div < 0x4)); 440dcdd3278SHeiko Stübner 441dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[10], 442dcdd3278SHeiko Stübner PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | 443dcdd3278SHeiko Stübner PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | 444dcdd3278SHeiko Stübner PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, 445dcdd3278SHeiko Stübner PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | 446dcdd3278SHeiko Stübner pclk_div << PERI_PCLK_DIV_SHIFT | 447dcdd3278SHeiko Stübner hclk_div << PERI_HCLK_DIV_SHIFT | 448dcdd3278SHeiko Stübner aclk_div << PERI_ACLK_DIV_SHIFT); 449dcdd3278SHeiko Stübner 450dcdd3278SHeiko Stübner /* PLL enter normal-mode */ 451dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, 452dcdd3278SHeiko Stübner GPLL_MODE_MASK << GPLL_MODE_SHIFT | 453dcdd3278SHeiko Stübner CPLL_MODE_MASK << CPLL_MODE_SHIFT, 454dcdd3278SHeiko Stübner GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | 455dcdd3278SHeiko Stübner CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); 456dcdd3278SHeiko Stübner 457dcdd3278SHeiko Stübner rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000); 458dcdd3278SHeiko Stübner } 459dcdd3278SHeiko Stübner #endif 460dcdd3278SHeiko Stübner 461dcdd3278SHeiko Stübner static ulong rk3188_clk_get_rate(struct clk *clk) 462dcdd3278SHeiko Stübner { 463dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(clk->dev); 464dcdd3278SHeiko Stübner ulong new_rate, gclk_rate; 465dcdd3278SHeiko Stübner 466dcdd3278SHeiko Stübner gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); 467dcdd3278SHeiko Stübner switch (clk->id) { 468dcdd3278SHeiko Stübner case 1 ... 4: 469dcdd3278SHeiko Stübner new_rate = rkclk_pll_get_rate(priv->cru, clk->id); 470dcdd3278SHeiko Stübner break; 471dcdd3278SHeiko Stübner case HCLK_EMMC: 472dcdd3278SHeiko Stübner case HCLK_SDMMC: 473dcdd3278SHeiko Stübner case HCLK_SDIO: 4747a25a63cSXu Ziyuan case SCLK_EMMC: 4757a25a63cSXu Ziyuan case SCLK_SDMMC: 4767a25a63cSXu Ziyuan case SCLK_SDIO: 477dcdd3278SHeiko Stübner new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ, 478dcdd3278SHeiko Stübner clk->id); 479dcdd3278SHeiko Stübner break; 480dcdd3278SHeiko Stübner case SCLK_SPI0: 481dcdd3278SHeiko Stübner case SCLK_SPI1: 482dcdd3278SHeiko Stübner new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ, 483dcdd3278SHeiko Stübner clk->id); 484dcdd3278SHeiko Stübner break; 485dcdd3278SHeiko Stübner case PCLK_I2C0: 486dcdd3278SHeiko Stübner case PCLK_I2C1: 487dcdd3278SHeiko Stübner case PCLK_I2C2: 488dcdd3278SHeiko Stübner case PCLK_I2C3: 489dcdd3278SHeiko Stübner case PCLK_I2C4: 490dcdd3278SHeiko Stübner return gclk_rate; 491dcdd3278SHeiko Stübner default: 492dcdd3278SHeiko Stübner return -ENOENT; 493dcdd3278SHeiko Stübner } 494dcdd3278SHeiko Stübner 495dcdd3278SHeiko Stübner return new_rate; 496dcdd3278SHeiko Stübner } 497dcdd3278SHeiko Stübner 498dcdd3278SHeiko Stübner static ulong rk3188_clk_set_rate(struct clk *clk, ulong rate) 499dcdd3278SHeiko Stübner { 500dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(clk->dev); 501dcdd3278SHeiko Stübner struct rk3188_cru *cru = priv->cru; 502dcdd3278SHeiko Stübner ulong new_rate; 503dcdd3278SHeiko Stübner 504dcdd3278SHeiko Stübner switch (clk->id) { 505f7853570SHeiko Stübner case PLL_APLL: 506f7853570SHeiko Stübner new_rate = rkclk_configure_cpu(priv->cru, priv->grf, rate, 507f7853570SHeiko Stübner priv->has_bwadj); 508f7853570SHeiko Stübner break; 509dcdd3278SHeiko Stübner case CLK_DDR: 510dcdd3278SHeiko Stübner new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate, 511dcdd3278SHeiko Stübner priv->has_bwadj); 512dcdd3278SHeiko Stübner break; 513dcdd3278SHeiko Stübner case HCLK_EMMC: 514dcdd3278SHeiko Stübner case HCLK_SDMMC: 515dcdd3278SHeiko Stübner case HCLK_SDIO: 5167a25a63cSXu Ziyuan case SCLK_EMMC: 5177a25a63cSXu Ziyuan case SCLK_SDMMC: 5187a25a63cSXu Ziyuan case SCLK_SDIO: 519dcdd3278SHeiko Stübner new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, 520dcdd3278SHeiko Stübner clk->id, rate); 521dcdd3278SHeiko Stübner break; 522dcdd3278SHeiko Stübner case SCLK_SPI0: 523dcdd3278SHeiko Stübner case SCLK_SPI1: 524dcdd3278SHeiko Stübner new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ, 525dcdd3278SHeiko Stübner clk->id, rate); 526dcdd3278SHeiko Stübner break; 527dcdd3278SHeiko Stübner default: 528dcdd3278SHeiko Stübner return -ENOENT; 529dcdd3278SHeiko Stübner } 530dcdd3278SHeiko Stübner 531dcdd3278SHeiko Stübner return new_rate; 532dcdd3278SHeiko Stübner } 533dcdd3278SHeiko Stübner 534dcdd3278SHeiko Stübner static struct clk_ops rk3188_clk_ops = { 535dcdd3278SHeiko Stübner .get_rate = rk3188_clk_get_rate, 536dcdd3278SHeiko Stübner .set_rate = rk3188_clk_set_rate, 537dcdd3278SHeiko Stübner }; 538dcdd3278SHeiko Stübner 539dcdd3278SHeiko Stübner static int rk3188_clk_ofdata_to_platdata(struct udevice *dev) 540dcdd3278SHeiko Stübner { 541dcdd3278SHeiko Stübner #if !CONFIG_IS_ENABLED(OF_PLATDATA) 542dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(dev); 543dcdd3278SHeiko Stübner 544*aca45647SKever Yang priv->cru = dev_read_addr_ptr(dev); 545dcdd3278SHeiko Stübner #endif 546dcdd3278SHeiko Stübner 547dcdd3278SHeiko Stübner return 0; 548dcdd3278SHeiko Stübner } 549dcdd3278SHeiko Stübner 550dcdd3278SHeiko Stübner static int rk3188_clk_probe(struct udevice *dev) 551dcdd3278SHeiko Stübner { 552dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(dev); 553dcdd3278SHeiko Stübner enum rk3188_clk_type type = dev_get_driver_data(dev); 554dcdd3278SHeiko Stübner 555dcdd3278SHeiko Stübner priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 556dcdd3278SHeiko Stübner if (IS_ERR(priv->grf)) 557dcdd3278SHeiko Stübner return PTR_ERR(priv->grf); 558dcdd3278SHeiko Stübner priv->has_bwadj = (type == RK3188A_CRU) ? 1 : 0; 559dcdd3278SHeiko Stübner 560dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 561dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA) 562dcdd3278SHeiko Stübner struct rk3188_clk_plat *plat = dev_get_platdata(dev); 563dcdd3278SHeiko Stübner 564dcdd3278SHeiko Stübner priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); 565dcdd3278SHeiko Stübner #endif 566dcdd3278SHeiko Stübner 567dcdd3278SHeiko Stübner rkclk_init(priv->cru, priv->grf, priv->has_bwadj); 568dcdd3278SHeiko Stübner #endif 569dcdd3278SHeiko Stübner 570dcdd3278SHeiko Stübner return 0; 571dcdd3278SHeiko Stübner } 572dcdd3278SHeiko Stübner 573dcdd3278SHeiko Stübner static int rk3188_clk_bind(struct udevice *dev) 574dcdd3278SHeiko Stübner { 575dcdd3278SHeiko Stübner int ret; 576f24e36daSKever Yang struct udevice *sys_child; 577f24e36daSKever Yang struct sysreset_reg *priv; 578dcdd3278SHeiko Stübner 579dcdd3278SHeiko Stübner /* The reset driver does not have a device node, so bind it here */ 580f24e36daSKever Yang ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", 581f24e36daSKever Yang &sys_child); 582f24e36daSKever Yang if (ret) { 583f24e36daSKever Yang debug("Warning: No sysreset driver: ret=%d\n", ret); 584f24e36daSKever Yang } else { 585f24e36daSKever Yang priv = malloc(sizeof(struct sysreset_reg)); 586f24e36daSKever Yang priv->glb_srst_fst_value = offsetof(struct rk3188_cru, 587f24e36daSKever Yang cru_glb_srst_fst_value); 588f24e36daSKever Yang priv->glb_srst_snd_value = offsetof(struct rk3188_cru, 589f24e36daSKever Yang cru_glb_srst_snd_value); 590f24e36daSKever Yang sys_child->priv = priv; 591f24e36daSKever Yang } 592dcdd3278SHeiko Stübner 593538f67c3SElaine Zhang #if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) 594538f67c3SElaine Zhang ret = offsetof(struct rk3188_cru, cru_softrst_con[0]); 595538f67c3SElaine Zhang ret = rockchip_reset_bind(dev, ret, 9); 596538f67c3SElaine Zhang if (ret) 597538f67c3SElaine Zhang debug("Warning: software reset driver bind faile\n"); 598538f67c3SElaine Zhang #endif 599538f67c3SElaine Zhang 600dcdd3278SHeiko Stübner return 0; 601dcdd3278SHeiko Stübner } 602dcdd3278SHeiko Stübner 603dcdd3278SHeiko Stübner static const struct udevice_id rk3188_clk_ids[] = { 604dcdd3278SHeiko Stübner { .compatible = "rockchip,rk3188-cru", .data = RK3188_CRU }, 605dcdd3278SHeiko Stübner { .compatible = "rockchip,rk3188a-cru", .data = RK3188A_CRU }, 606dcdd3278SHeiko Stübner { } 607dcdd3278SHeiko Stübner }; 608dcdd3278SHeiko Stübner 609dcdd3278SHeiko Stübner U_BOOT_DRIVER(rockchip_rk3188_cru) = { 610dcdd3278SHeiko Stübner .name = "rockchip_rk3188_cru", 611dcdd3278SHeiko Stübner .id = UCLASS_CLK, 612dcdd3278SHeiko Stübner .of_match = rk3188_clk_ids, 613dcdd3278SHeiko Stübner .priv_auto_alloc_size = sizeof(struct rk3188_clk_priv), 614dcdd3278SHeiko Stübner .platdata_auto_alloc_size = sizeof(struct rk3188_clk_plat), 615dcdd3278SHeiko Stübner .ops = &rk3188_clk_ops, 616dcdd3278SHeiko Stübner .bind = rk3188_clk_bind, 617dcdd3278SHeiko Stübner .ofdata_to_platdata = rk3188_clk_ofdata_to_platdata, 618dcdd3278SHeiko Stübner .probe = rk3188_clk_probe, 619dcdd3278SHeiko Stübner }; 620