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 RATE_TO_DIV(input_rate, output_rate) \ 75dcdd3278SHeiko Stübner ((input_rate) / (output_rate) - 1); 76dcdd3278SHeiko Stübner 77dcdd3278SHeiko Stübner #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) 78dcdd3278SHeiko Stübner 79dcdd3278SHeiko Stübner #define PLL_DIVISORS(hz, _nr, _no) {\ 80dcdd3278SHeiko Stübner .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\ 81dcdd3278SHeiko Stübner _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\ 82dcdd3278SHeiko Stübner (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\ 83dcdd3278SHeiko Stübner "divisors on line " __stringify(__LINE__)); 84dcdd3278SHeiko Stübner 85dcdd3278SHeiko Stübner /* Keep divisors as low as possible to reduce jitter and power usage */ 86dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 87dcdd3278SHeiko Stübner static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2); 88dcdd3278SHeiko Stübner static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2); 89dcdd3278SHeiko Stübner #endif 90dcdd3278SHeiko Stübner 91dcdd3278SHeiko Stübner static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id, 92dcdd3278SHeiko Stübner const struct pll_div *div, bool has_bwadj) 93dcdd3278SHeiko Stübner { 94dcdd3278SHeiko Stübner int pll_id = rk_pll_id(clk_id); 95dcdd3278SHeiko Stübner struct rk3188_pll *pll = &cru->pll[pll_id]; 96dcdd3278SHeiko Stübner /* All PLLs have same VCO and output frequency range restrictions. */ 97dcdd3278SHeiko Stübner uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000; 98dcdd3278SHeiko Stübner uint output_hz = vco_hz / div->no; 99dcdd3278SHeiko Stübner 100dcdd3278SHeiko Stübner debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", 101dcdd3278SHeiko Stübner (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz); 102dcdd3278SHeiko Stübner assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && 103dcdd3278SHeiko Stübner output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ && 104dcdd3278SHeiko Stübner (div->no == 1 || !(div->no % 2))); 105dcdd3278SHeiko Stübner 106dcdd3278SHeiko Stübner /* enter reset */ 107dcdd3278SHeiko Stübner rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT); 108dcdd3278SHeiko Stübner 109dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con0, 110dcdd3278SHeiko Stübner CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK, 111dcdd3278SHeiko Stübner ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1)); 112dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1); 113dcdd3278SHeiko Stübner 114dcdd3278SHeiko Stübner if (has_bwadj) 115dcdd3278SHeiko Stübner rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); 116dcdd3278SHeiko Stübner 117dcdd3278SHeiko Stübner udelay(10); 118dcdd3278SHeiko Stübner 119dcdd3278SHeiko Stübner /* return from reset */ 120dcdd3278SHeiko Stübner rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT); 121dcdd3278SHeiko Stübner 122dcdd3278SHeiko Stübner return 0; 123dcdd3278SHeiko Stübner } 124dcdd3278SHeiko Stübner 125dcdd3278SHeiko Stübner static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf, 126dcdd3278SHeiko Stübner unsigned int hz, bool has_bwadj) 127dcdd3278SHeiko Stübner { 128dcdd3278SHeiko Stübner static const struct pll_div dpll_cfg[] = { 129dcdd3278SHeiko Stübner {.nf = 25, .nr = 2, .no = 1}, 130dcdd3278SHeiko Stübner {.nf = 400, .nr = 9, .no = 2}, 131dcdd3278SHeiko Stübner {.nf = 500, .nr = 9, .no = 2}, 132dcdd3278SHeiko Stübner {.nf = 100, .nr = 3, .no = 1}, 133dcdd3278SHeiko Stübner }; 134dcdd3278SHeiko Stübner int cfg; 135dcdd3278SHeiko Stübner 136dcdd3278SHeiko Stübner switch (hz) { 137dcdd3278SHeiko Stübner case 300000000: 138dcdd3278SHeiko Stübner cfg = 0; 139dcdd3278SHeiko Stübner break; 140dcdd3278SHeiko Stübner case 533000000: /* actually 533.3P MHz */ 141dcdd3278SHeiko Stübner cfg = 1; 142dcdd3278SHeiko Stübner break; 143dcdd3278SHeiko Stübner case 666000000: /* actually 666.6P MHz */ 144dcdd3278SHeiko Stübner cfg = 2; 145dcdd3278SHeiko Stübner break; 146dcdd3278SHeiko Stübner case 800000000: 147dcdd3278SHeiko Stübner cfg = 3; 148dcdd3278SHeiko Stübner break; 149dcdd3278SHeiko Stübner default: 150dcdd3278SHeiko Stübner debug("Unsupported SDRAM frequency"); 151dcdd3278SHeiko Stübner return -EINVAL; 152dcdd3278SHeiko Stübner } 153dcdd3278SHeiko Stübner 154dcdd3278SHeiko Stübner /* pll enter slow-mode */ 155dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, 156dcdd3278SHeiko Stübner DPLL_MODE_SLOW << DPLL_MODE_SHIFT); 157dcdd3278SHeiko Stübner 158dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj); 159dcdd3278SHeiko Stübner 160dcdd3278SHeiko Stübner /* wait for pll lock */ 161dcdd3278SHeiko Stübner while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK)) 162dcdd3278SHeiko Stübner udelay(1); 163dcdd3278SHeiko Stübner 164dcdd3278SHeiko Stübner /* PLL enter normal-mode */ 165dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, 166dcdd3278SHeiko Stübner DPLL_MODE_NORMAL << DPLL_MODE_SHIFT); 167dcdd3278SHeiko Stübner 168dcdd3278SHeiko Stübner return 0; 169dcdd3278SHeiko Stübner } 170dcdd3278SHeiko Stübner 171*f7853570SHeiko Stübner static int rkclk_configure_cpu(struct rk3188_cru *cru, struct rk3188_grf *grf, 172*f7853570SHeiko Stübner unsigned int hz, bool has_bwadj) 173*f7853570SHeiko Stübner { 174*f7853570SHeiko Stübner static const struct pll_div apll_cfg[] = { 175*f7853570SHeiko Stübner {.nf = 50, .nr = 1, .no = 2}, 176*f7853570SHeiko Stübner {.nf = 67, .nr = 1, .no = 1}, 177*f7853570SHeiko Stübner }; 178*f7853570SHeiko Stübner int div_core_peri, div_aclk_core, cfg; 179*f7853570SHeiko Stübner 180*f7853570SHeiko Stübner /* 181*f7853570SHeiko Stübner * We support two possible frequencies, the safe 600MHz 182*f7853570SHeiko Stübner * which will work with default pmic settings and will 183*f7853570SHeiko Stübner * be set in SPL to get away from the 24MHz default and 184*f7853570SHeiko Stübner * the maximum of 1.6Ghz, which boards can set if they 185*f7853570SHeiko Stübner * were able to get pmic support for it. 186*f7853570SHeiko Stübner */ 187*f7853570SHeiko Stübner switch (hz) { 188*f7853570SHeiko Stübner case APLL_SAFE_HZ: 189*f7853570SHeiko Stübner cfg = 0; 190*f7853570SHeiko Stübner div_core_peri = 1; 191*f7853570SHeiko Stübner div_aclk_core = 3; 192*f7853570SHeiko Stübner break; 193*f7853570SHeiko Stübner case APLL_HZ: 194*f7853570SHeiko Stübner cfg = 1; 195*f7853570SHeiko Stübner div_core_peri = 2; 196*f7853570SHeiko Stübner div_aclk_core = 3; 197*f7853570SHeiko Stübner break; 198*f7853570SHeiko Stübner default: 199*f7853570SHeiko Stübner debug("Unsupported ARMCLK frequency"); 200*f7853570SHeiko Stübner return -EINVAL; 201*f7853570SHeiko Stübner } 202*f7853570SHeiko Stübner 203*f7853570SHeiko Stübner /* pll enter slow-mode */ 204*f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT, 205*f7853570SHeiko Stübner APLL_MODE_SLOW << APLL_MODE_SHIFT); 206*f7853570SHeiko Stübner 207*f7853570SHeiko Stübner rkclk_set_pll(cru, CLK_ARM, &apll_cfg[cfg], has_bwadj); 208*f7853570SHeiko Stübner 209*f7853570SHeiko Stübner /* waiting for pll lock */ 210*f7853570SHeiko Stübner while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK)) 211*f7853570SHeiko Stübner udelay(1); 212*f7853570SHeiko Stübner 213*f7853570SHeiko Stübner /* Set divider for peripherals attached to the cpu core. */ 214*f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[0], 215*f7853570SHeiko Stübner CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT, 216*f7853570SHeiko Stübner div_core_peri << CORE_PERI_DIV_SHIFT); 217*f7853570SHeiko Stübner 218*f7853570SHeiko Stübner /* set up dependent divisor for aclk_core */ 219*f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[1], 220*f7853570SHeiko Stübner CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT, 221*f7853570SHeiko Stübner div_aclk_core << CORE_ACLK_DIV_SHIFT); 222*f7853570SHeiko Stübner 223*f7853570SHeiko Stübner /* PLL enter normal-mode */ 224*f7853570SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT, 225*f7853570SHeiko Stübner APLL_MODE_NORMAL << APLL_MODE_SHIFT); 226*f7853570SHeiko Stübner 227*f7853570SHeiko Stübner return hz; 228*f7853570SHeiko Stübner } 229*f7853570SHeiko Stübner 230dcdd3278SHeiko Stübner /* Get pll rate by id */ 231dcdd3278SHeiko Stübner static uint32_t rkclk_pll_get_rate(struct rk3188_cru *cru, 232dcdd3278SHeiko Stübner enum rk_clk_id clk_id) 233dcdd3278SHeiko Stübner { 234dcdd3278SHeiko Stübner uint32_t nr, no, nf; 235dcdd3278SHeiko Stübner uint32_t con; 236dcdd3278SHeiko Stübner int pll_id = rk_pll_id(clk_id); 237dcdd3278SHeiko Stübner struct rk3188_pll *pll = &cru->pll[pll_id]; 238dcdd3278SHeiko Stübner static u8 clk_shift[CLK_COUNT] = { 239dcdd3278SHeiko Stübner 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, 240dcdd3278SHeiko Stübner GPLL_MODE_SHIFT 241dcdd3278SHeiko Stübner }; 242dcdd3278SHeiko Stübner uint shift; 243dcdd3278SHeiko Stübner 244dcdd3278SHeiko Stübner con = readl(&cru->cru_mode_con); 245dcdd3278SHeiko Stübner shift = clk_shift[clk_id]; 246dcdd3278SHeiko Stübner switch ((con >> shift) & APLL_MODE_MASK) { 247dcdd3278SHeiko Stübner case APLL_MODE_SLOW: 248dcdd3278SHeiko Stübner return OSC_HZ; 249dcdd3278SHeiko Stübner case APLL_MODE_NORMAL: 250dcdd3278SHeiko Stübner /* normal mode */ 251dcdd3278SHeiko Stübner con = readl(&pll->con0); 252dcdd3278SHeiko Stübner no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; 253dcdd3278SHeiko Stübner nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1; 254dcdd3278SHeiko Stübner con = readl(&pll->con1); 255dcdd3278SHeiko Stübner nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; 256dcdd3278SHeiko Stübner 257dcdd3278SHeiko Stübner return (24 * nf / (nr * no)) * 1000000; 258dcdd3278SHeiko Stübner case APLL_MODE_DEEP: 259dcdd3278SHeiko Stübner default: 260dcdd3278SHeiko Stübner return 32768; 261dcdd3278SHeiko Stübner } 262dcdd3278SHeiko Stübner } 263dcdd3278SHeiko Stübner 264dcdd3278SHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate, 265dcdd3278SHeiko Stübner int periph) 266dcdd3278SHeiko Stübner { 267dcdd3278SHeiko Stübner uint div; 268dcdd3278SHeiko Stübner u32 con; 269dcdd3278SHeiko Stübner 270dcdd3278SHeiko Stübner switch (periph) { 271dcdd3278SHeiko Stübner case HCLK_EMMC: 272dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[12]); 273dcdd3278SHeiko Stübner div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; 274dcdd3278SHeiko Stübner break; 275dcdd3278SHeiko Stübner case HCLK_SDMMC: 276dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[11]); 277dcdd3278SHeiko Stübner div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; 278dcdd3278SHeiko Stübner break; 279dcdd3278SHeiko Stübner case HCLK_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 287dcdd3278SHeiko Stübner return DIV_TO_RATE(gclk_rate, div); 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); 296dcdd3278SHeiko Stübner src_clk_div = RATE_TO_DIV(gclk_rate, freq); 297dcdd3278SHeiko Stübner assert(src_clk_div <= 0x3f); 298dcdd3278SHeiko Stübner 299dcdd3278SHeiko Stübner switch (periph) { 300dcdd3278SHeiko Stübner case HCLK_EMMC: 301dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12], 302dcdd3278SHeiko Stübner EMMC_DIV_MASK << EMMC_DIV_SHIFT, 303dcdd3278SHeiko Stübner src_clk_div << EMMC_DIV_SHIFT); 304dcdd3278SHeiko Stübner break; 305dcdd3278SHeiko Stübner case HCLK_SDMMC: 306dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[11], 307dcdd3278SHeiko Stübner MMC0_DIV_MASK << MMC0_DIV_SHIFT, 308dcdd3278SHeiko Stübner src_clk_div << MMC0_DIV_SHIFT); 309dcdd3278SHeiko Stübner break; 310dcdd3278SHeiko Stübner case HCLK_SDIO: 311dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12], 312dcdd3278SHeiko Stübner SDIO_DIV_MASK << SDIO_DIV_SHIFT, 313dcdd3278SHeiko Stübner src_clk_div << SDIO_DIV_SHIFT); 314dcdd3278SHeiko Stübner break; 315dcdd3278SHeiko Stübner default: 316dcdd3278SHeiko Stübner return -EINVAL; 317dcdd3278SHeiko Stübner } 318dcdd3278SHeiko Stübner 319dcdd3278SHeiko Stübner return rockchip_mmc_get_clk(cru, gclk_rate, periph); 320dcdd3278SHeiko Stübner } 321dcdd3278SHeiko Stübner 322dcdd3278SHeiko Stübner static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate, 323dcdd3278SHeiko Stübner int periph) 324dcdd3278SHeiko Stübner { 325dcdd3278SHeiko Stübner uint div; 326dcdd3278SHeiko Stübner u32 con; 327dcdd3278SHeiko Stübner 328dcdd3278SHeiko Stübner switch (periph) { 329dcdd3278SHeiko Stübner case SCLK_SPI0: 330dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[25]); 331dcdd3278SHeiko Stübner div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK; 332dcdd3278SHeiko Stübner break; 333dcdd3278SHeiko Stübner case SCLK_SPI1: 334dcdd3278SHeiko Stübner con = readl(&cru->cru_clksel_con[25]); 335dcdd3278SHeiko Stübner div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK; 336dcdd3278SHeiko Stübner break; 337dcdd3278SHeiko Stübner default: 338dcdd3278SHeiko Stübner return -EINVAL; 339dcdd3278SHeiko Stübner } 340dcdd3278SHeiko Stübner 341dcdd3278SHeiko Stübner return DIV_TO_RATE(gclk_rate, div); 342dcdd3278SHeiko Stübner } 343dcdd3278SHeiko Stübner 344dcdd3278SHeiko Stübner static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate, 345dcdd3278SHeiko Stübner int periph, uint freq) 346dcdd3278SHeiko Stübner { 347dcdd3278SHeiko Stübner int src_clk_div = RATE_TO_DIV(gclk_rate, freq); 348dcdd3278SHeiko Stübner 349dcdd3278SHeiko Stübner switch (periph) { 350dcdd3278SHeiko Stübner case SCLK_SPI0: 351dcdd3278SHeiko Stübner assert(src_clk_div <= SPI0_DIV_MASK); 352dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25], 353dcdd3278SHeiko Stübner SPI0_DIV_MASK << SPI0_DIV_SHIFT, 354dcdd3278SHeiko Stübner src_clk_div << SPI0_DIV_SHIFT); 355dcdd3278SHeiko Stübner break; 356dcdd3278SHeiko Stübner case SCLK_SPI1: 357dcdd3278SHeiko Stübner assert(src_clk_div <= SPI1_DIV_MASK); 358dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25], 359dcdd3278SHeiko Stübner SPI1_DIV_MASK << SPI1_DIV_SHIFT, 360dcdd3278SHeiko Stübner src_clk_div << SPI1_DIV_SHIFT); 361dcdd3278SHeiko Stübner break; 362dcdd3278SHeiko Stübner default: 363dcdd3278SHeiko Stübner return -EINVAL; 364dcdd3278SHeiko Stübner } 365dcdd3278SHeiko Stübner 366dcdd3278SHeiko Stübner return rockchip_spi_get_clk(cru, gclk_rate, periph); 367dcdd3278SHeiko Stübner } 368dcdd3278SHeiko Stübner 369dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 370dcdd3278SHeiko Stübner static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf, 371dcdd3278SHeiko Stübner bool has_bwadj) 372dcdd3278SHeiko Stübner { 373dcdd3278SHeiko Stübner u32 aclk_div, hclk_div, pclk_div, h2p_div; 374dcdd3278SHeiko Stübner 375dcdd3278SHeiko Stübner /* pll enter slow-mode */ 376dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, 377dcdd3278SHeiko Stübner GPLL_MODE_MASK << GPLL_MODE_SHIFT | 378dcdd3278SHeiko Stübner CPLL_MODE_MASK << CPLL_MODE_SHIFT, 379dcdd3278SHeiko Stübner GPLL_MODE_SLOW << GPLL_MODE_SHIFT | 380dcdd3278SHeiko Stübner CPLL_MODE_SLOW << CPLL_MODE_SHIFT); 381dcdd3278SHeiko Stübner 382dcdd3278SHeiko Stübner /* init pll */ 383dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj); 384dcdd3278SHeiko Stübner rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj); 385dcdd3278SHeiko Stübner 386dcdd3278SHeiko Stübner /* waiting for pll lock */ 387dcdd3278SHeiko Stübner while ((readl(&grf->soc_status0) & 388dcdd3278SHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) != 389dcdd3278SHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) 390dcdd3278SHeiko Stübner udelay(1); 391dcdd3278SHeiko Stübner 392dcdd3278SHeiko Stübner /* 393dcdd3278SHeiko Stübner * cpu clock pll source selection and 394dcdd3278SHeiko Stübner * reparent aclk_cpu_pre from apll to gpll 395dcdd3278SHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks. 396dcdd3278SHeiko Stübner */ 397dcdd3278SHeiko Stübner aclk_div = RATE_TO_DIV(GPLL_HZ, CPU_ACLK_HZ); 398dcdd3278SHeiko Stübner assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); 399dcdd3278SHeiko Stübner 400dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[0], 401dcdd3278SHeiko Stübner CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT | 402dcdd3278SHeiko Stübner A9_CPU_DIV_MASK << A9_CPU_DIV_SHIFT, 403dcdd3278SHeiko Stübner CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT | 404dcdd3278SHeiko Stübner aclk_div << A9_CPU_DIV_SHIFT); 405dcdd3278SHeiko Stübner 406dcdd3278SHeiko Stübner hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ); 407dcdd3278SHeiko Stübner assert((1 << hclk_div) * CPU_HCLK_HZ == CPU_ACLK_HZ && hclk_div < 0x3); 408dcdd3278SHeiko Stübner pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ); 409dcdd3278SHeiko Stübner assert((1 << pclk_div) * CPU_PCLK_HZ == CPU_ACLK_HZ && pclk_div < 0x4); 410dcdd3278SHeiko Stübner h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ); 411dcdd3278SHeiko Stübner assert((1 << h2p_div) * CPU_H2P_HZ == CPU_HCLK_HZ && pclk_div < 0x3); 412dcdd3278SHeiko Stübner 413dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[1], 414dcdd3278SHeiko Stübner AHB2APB_DIV_MASK << AHB2APB_DIV_SHIFT | 415dcdd3278SHeiko Stübner CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT | 416dcdd3278SHeiko Stübner CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT, 417dcdd3278SHeiko Stübner h2p_div << AHB2APB_DIV_SHIFT | 418dcdd3278SHeiko Stübner pclk_div << CPU_PCLK_DIV_SHIFT | 419dcdd3278SHeiko Stübner hclk_div << CPU_HCLK_DIV_SHIFT); 420dcdd3278SHeiko Stübner 421dcdd3278SHeiko Stübner /* 422dcdd3278SHeiko Stübner * peri clock pll source selection and 423dcdd3278SHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks. 424dcdd3278SHeiko Stübner */ 425dcdd3278SHeiko Stübner aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; 426dcdd3278SHeiko Stübner assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); 427dcdd3278SHeiko Stübner 428dcdd3278SHeiko Stübner hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ); 429dcdd3278SHeiko Stübner assert((1 << hclk_div) * PERI_HCLK_HZ == 430dcdd3278SHeiko Stübner PERI_ACLK_HZ && (hclk_div < 0x4)); 431dcdd3278SHeiko Stübner 432dcdd3278SHeiko Stübner pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ); 433dcdd3278SHeiko Stübner assert((1 << pclk_div) * PERI_PCLK_HZ == 434dcdd3278SHeiko Stübner PERI_ACLK_HZ && (pclk_div < 0x4)); 435dcdd3278SHeiko Stübner 436dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[10], 437dcdd3278SHeiko Stübner PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | 438dcdd3278SHeiko Stübner PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | 439dcdd3278SHeiko Stübner PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, 440dcdd3278SHeiko Stübner PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | 441dcdd3278SHeiko Stübner pclk_div << PERI_PCLK_DIV_SHIFT | 442dcdd3278SHeiko Stübner hclk_div << PERI_HCLK_DIV_SHIFT | 443dcdd3278SHeiko Stübner aclk_div << PERI_ACLK_DIV_SHIFT); 444dcdd3278SHeiko Stübner 445dcdd3278SHeiko Stübner /* PLL enter normal-mode */ 446dcdd3278SHeiko Stübner rk_clrsetreg(&cru->cru_mode_con, 447dcdd3278SHeiko Stübner GPLL_MODE_MASK << GPLL_MODE_SHIFT | 448dcdd3278SHeiko Stübner CPLL_MODE_MASK << CPLL_MODE_SHIFT, 449dcdd3278SHeiko Stübner GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | 450dcdd3278SHeiko Stübner CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); 451dcdd3278SHeiko Stübner 452dcdd3278SHeiko Stübner rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000); 453dcdd3278SHeiko Stübner } 454dcdd3278SHeiko Stübner #endif 455dcdd3278SHeiko Stübner 456dcdd3278SHeiko Stübner static ulong rk3188_clk_get_rate(struct clk *clk) 457dcdd3278SHeiko Stübner { 458dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(clk->dev); 459dcdd3278SHeiko Stübner ulong new_rate, gclk_rate; 460dcdd3278SHeiko Stübner 461dcdd3278SHeiko Stübner gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); 462dcdd3278SHeiko Stübner switch (clk->id) { 463dcdd3278SHeiko Stübner case 1 ... 4: 464dcdd3278SHeiko Stübner new_rate = rkclk_pll_get_rate(priv->cru, clk->id); 465dcdd3278SHeiko Stübner break; 466dcdd3278SHeiko Stübner case HCLK_EMMC: 467dcdd3278SHeiko Stübner case HCLK_SDMMC: 468dcdd3278SHeiko Stübner case HCLK_SDIO: 469dcdd3278SHeiko Stübner new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ, 470dcdd3278SHeiko Stübner clk->id); 471dcdd3278SHeiko Stübner break; 472dcdd3278SHeiko Stübner case SCLK_SPI0: 473dcdd3278SHeiko Stübner case SCLK_SPI1: 474dcdd3278SHeiko Stübner new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ, 475dcdd3278SHeiko Stübner clk->id); 476dcdd3278SHeiko Stübner break; 477dcdd3278SHeiko Stübner case PCLK_I2C0: 478dcdd3278SHeiko Stübner case PCLK_I2C1: 479dcdd3278SHeiko Stübner case PCLK_I2C2: 480dcdd3278SHeiko Stübner case PCLK_I2C3: 481dcdd3278SHeiko Stübner case PCLK_I2C4: 482dcdd3278SHeiko Stübner return gclk_rate; 483dcdd3278SHeiko Stübner default: 484dcdd3278SHeiko Stübner return -ENOENT; 485dcdd3278SHeiko Stübner } 486dcdd3278SHeiko Stübner 487dcdd3278SHeiko Stübner return new_rate; 488dcdd3278SHeiko Stübner } 489dcdd3278SHeiko Stübner 490dcdd3278SHeiko Stübner static ulong rk3188_clk_set_rate(struct clk *clk, ulong rate) 491dcdd3278SHeiko Stübner { 492dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(clk->dev); 493dcdd3278SHeiko Stübner struct rk3188_cru *cru = priv->cru; 494dcdd3278SHeiko Stübner ulong new_rate; 495dcdd3278SHeiko Stübner 496dcdd3278SHeiko Stübner switch (clk->id) { 497*f7853570SHeiko Stübner case PLL_APLL: 498*f7853570SHeiko Stübner new_rate = rkclk_configure_cpu(priv->cru, priv->grf, rate, 499*f7853570SHeiko Stübner priv->has_bwadj); 500*f7853570SHeiko Stübner break; 501dcdd3278SHeiko Stübner case CLK_DDR: 502dcdd3278SHeiko Stübner new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate, 503dcdd3278SHeiko Stübner priv->has_bwadj); 504dcdd3278SHeiko Stübner break; 505dcdd3278SHeiko Stübner case HCLK_EMMC: 506dcdd3278SHeiko Stübner case HCLK_SDMMC: 507dcdd3278SHeiko Stübner case HCLK_SDIO: 508dcdd3278SHeiko Stübner new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, 509dcdd3278SHeiko Stübner clk->id, rate); 510dcdd3278SHeiko Stübner break; 511dcdd3278SHeiko Stübner case SCLK_SPI0: 512dcdd3278SHeiko Stübner case SCLK_SPI1: 513dcdd3278SHeiko Stübner new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ, 514dcdd3278SHeiko Stübner clk->id, rate); 515dcdd3278SHeiko Stübner break; 516dcdd3278SHeiko Stübner default: 517dcdd3278SHeiko Stübner return -ENOENT; 518dcdd3278SHeiko Stübner } 519dcdd3278SHeiko Stübner 520dcdd3278SHeiko Stübner return new_rate; 521dcdd3278SHeiko Stübner } 522dcdd3278SHeiko Stübner 523dcdd3278SHeiko Stübner static struct clk_ops rk3188_clk_ops = { 524dcdd3278SHeiko Stübner .get_rate = rk3188_clk_get_rate, 525dcdd3278SHeiko Stübner .set_rate = rk3188_clk_set_rate, 526dcdd3278SHeiko Stübner }; 527dcdd3278SHeiko Stübner 528dcdd3278SHeiko Stübner static int rk3188_clk_ofdata_to_platdata(struct udevice *dev) 529dcdd3278SHeiko Stübner { 530dcdd3278SHeiko Stübner #if !CONFIG_IS_ENABLED(OF_PLATDATA) 531dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(dev); 532dcdd3278SHeiko Stübner 533dcdd3278SHeiko Stübner priv->cru = (struct rk3188_cru *)dev_get_addr(dev); 534dcdd3278SHeiko Stübner #endif 535dcdd3278SHeiko Stübner 536dcdd3278SHeiko Stübner return 0; 537dcdd3278SHeiko Stübner } 538dcdd3278SHeiko Stübner 539dcdd3278SHeiko Stübner static int rk3188_clk_probe(struct udevice *dev) 540dcdd3278SHeiko Stübner { 541dcdd3278SHeiko Stübner struct rk3188_clk_priv *priv = dev_get_priv(dev); 542dcdd3278SHeiko Stübner enum rk3188_clk_type type = dev_get_driver_data(dev); 543dcdd3278SHeiko Stübner 544dcdd3278SHeiko Stübner priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 545dcdd3278SHeiko Stübner if (IS_ERR(priv->grf)) 546dcdd3278SHeiko Stübner return PTR_ERR(priv->grf); 547dcdd3278SHeiko Stübner priv->has_bwadj = (type == RK3188A_CRU) ? 1 : 0; 548dcdd3278SHeiko Stübner 549dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD 550dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA) 551dcdd3278SHeiko Stübner struct rk3188_clk_plat *plat = dev_get_platdata(dev); 552dcdd3278SHeiko Stübner 553dcdd3278SHeiko Stübner priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); 554dcdd3278SHeiko Stübner #endif 555dcdd3278SHeiko Stübner 556dcdd3278SHeiko Stübner rkclk_init(priv->cru, priv->grf, priv->has_bwadj); 557dcdd3278SHeiko Stübner #endif 558dcdd3278SHeiko Stübner 559dcdd3278SHeiko Stübner return 0; 560dcdd3278SHeiko Stübner } 561dcdd3278SHeiko Stübner 562dcdd3278SHeiko Stübner static int rk3188_clk_bind(struct udevice *dev) 563dcdd3278SHeiko Stübner { 564dcdd3278SHeiko Stübner int ret; 565dcdd3278SHeiko Stübner 566dcdd3278SHeiko Stübner /* The reset driver does not have a device node, so bind it here */ 567dcdd3278SHeiko Stübner ret = device_bind_driver(gd->dm_root, "rk3188_sysreset", "reset", &dev); 568dcdd3278SHeiko Stübner if (ret) 569dcdd3278SHeiko Stübner debug("Warning: No rk3188 reset driver: ret=%d\n", ret); 570dcdd3278SHeiko Stübner 571dcdd3278SHeiko Stübner return 0; 572dcdd3278SHeiko Stübner } 573dcdd3278SHeiko Stübner 574dcdd3278SHeiko Stübner static const struct udevice_id rk3188_clk_ids[] = { 575dcdd3278SHeiko Stübner { .compatible = "rockchip,rk3188-cru", .data = RK3188_CRU }, 576dcdd3278SHeiko Stübner { .compatible = "rockchip,rk3188a-cru", .data = RK3188A_CRU }, 577dcdd3278SHeiko Stübner { } 578dcdd3278SHeiko Stübner }; 579dcdd3278SHeiko Stübner 580dcdd3278SHeiko Stübner U_BOOT_DRIVER(rockchip_rk3188_cru) = { 581dcdd3278SHeiko Stübner .name = "rockchip_rk3188_cru", 582dcdd3278SHeiko Stübner .id = UCLASS_CLK, 583dcdd3278SHeiko Stübner .of_match = rk3188_clk_ids, 584dcdd3278SHeiko Stübner .priv_auto_alloc_size = sizeof(struct rk3188_clk_priv), 585dcdd3278SHeiko Stübner .platdata_auto_alloc_size = sizeof(struct rk3188_clk_plat), 586dcdd3278SHeiko Stübner .ops = &rk3188_clk_ops, 587dcdd3278SHeiko Stübner .bind = rk3188_clk_bind, 588dcdd3278SHeiko Stübner .ofdata_to_platdata = rk3188_clk_ofdata_to_platdata, 589dcdd3278SHeiko Stübner .probe = rk3188_clk_probe, 590dcdd3278SHeiko Stübner }; 591