// SPDX-License-Identifier: GPL-2.0 /* * (C) Copyright 2017 Rockchip Electronics Co., Ltd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct pll_div { u32 refdiv; u32 fbdiv; u32 postdiv1; u32 postdiv2; u32 frac; }; #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 const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 4, 1); static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 2, 1); static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1); static const struct pll_div apll_600_cfg = PLL_DIVISORS(600 * MHz, 1, 3, 1); static const struct pll_div *apll_cfgs[] = { [APLL_816_MHZ] = &apll_816_cfg, [APLL_600_MHZ] = &apll_600_cfg, }; enum { /* PLL_CON0 */ PLL_POSTDIV1_SHIFT = 12, PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT, PLL_FBDIV_SHIFT = 0, PLL_FBDIV_MASK = 0xfff, /* PLL_CON1 */ PLL_DSMPD_SHIFT = 12, PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, PLL_INTEGER_MODE = 1, PLL_LOCK_STATUS_SHIFT = 10, PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, PLL_POSTDIV2_SHIFT = 6, PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, PLL_REFDIV_SHIFT = 0, PLL_REFDIV_MASK = 0x3f, /* PLL_CON2 */ PLL_FRACDIV_SHIFT = 0, PLL_FRACDIV_MASK = 0xffffff, /* MODE_CON */ APLL_MODE_SHIFT = 0, NPLL_MODE_SHIFT = 1, DPLL_MODE_SHIFT = 4, CPLL_MODE_SHIFT = 8, GPLL_MODE_SHIFT = 12, PLL_MODE_SLOW = 0, PLL_MODE_NORM, /* CLKSEL_CON0 */ CLK_CORE_PLL_SEL_APLL = 0, CLK_CORE_PLL_SEL_GPLL, CLK_CORE_PLL_SEL_DPLL, CLK_CORE_PLL_SEL_NPLL, CLK_CORE_PLL_SEL_SHIFT = 6, CLK_CORE_PLL_SEL_MASK = 3 << CLK_CORE_PLL_SEL_SHIFT, CLK_CORE_DIV_SHIFT = 0, CLK_CORE_DIV_MASK = 0x1f, /* CLKSEL_CON1 */ ACLKM_CORE_DIV_SHIFT = 4, ACLKM_CORE_DIV_MASK = 0x7 << ACLKM_CORE_DIV_SHIFT, PCLK_DBG_DIV_SHIFT = 0, PCLK_DBG_DIV_MASK = 0xF << PCLK_DBG_DIV_SHIFT, /* CLKSEL_CON27 */ GMAC2IO_PLL_SEL_SHIFT = 7, GMAC2IO_PLL_SEL_MASK = 1 << GMAC2IO_PLL_SEL_SHIFT, GMAC2IO_PLL_SEL_CPLL = 0, GMAC2IO_PLL_SEL_GPLL = 1, GMAC2IO_CLK_DIV_MASK = 0x1f, GMAC2IO_CLK_DIV_SHIFT = 0, /* CLKSEL_CON28 */ ACLK_PERIHP_PLL_SEL_CPLL = 0, ACLK_PERIHP_PLL_SEL_GPLL, ACLK_PERIHP_PLL_SEL_HDMIPHY, ACLK_PERIHP_PLL_SEL_SHIFT = 6, ACLK_PERIHP_PLL_SEL_MASK = 3 << ACLK_PERIHP_PLL_SEL_SHIFT, ACLK_PERIHP_DIV_CON_SHIFT = 0, ACLK_PERIHP_DIV_CON_MASK = 0x1f, /* CLKSEL_CON29 */ PCLK_PERIHP_DIV_CON_SHIFT = 4, PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, HCLK_PERIHP_DIV_CON_SHIFT = 0, HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT, /* CLKSEL_CON22 */ CLK_TSADC_DIV_CON_SHIFT = 0, CLK_TSADC_DIV_CON_MASK = 0x3ff, /* CLKSEL_CON23 */ CLK_SARADC_DIV_CON_SHIFT = 0, CLK_SARADC_DIV_CON_MASK = GENMASK(9, 0), CLK_SARADC_DIV_CON_WIDTH = 10, /* CLKSEL_CON24 */ CLK_PWM_PLL_SEL_CPLL = 0, CLK_PWM_PLL_SEL_GPLL, CLK_PWM_PLL_SEL_SHIFT = 15, CLK_PWM_PLL_SEL_MASK = 1 << CLK_PWM_PLL_SEL_SHIFT, CLK_PWM_DIV_CON_SHIFT = 8, CLK_PWM_DIV_CON_MASK = 0x7f << CLK_PWM_DIV_CON_SHIFT, CLK_SPI_PLL_SEL_CPLL = 0, CLK_SPI_PLL_SEL_GPLL, CLK_SPI_PLL_SEL_SHIFT = 7, CLK_SPI_PLL_SEL_MASK = 1 << CLK_SPI_PLL_SEL_SHIFT, CLK_SPI_DIV_CON_SHIFT = 0, CLK_SPI_DIV_CON_MASK = 0x7f << CLK_SPI_DIV_CON_SHIFT, /* CLKSEL_CON30 */ CLK_SDMMC_PLL_SEL_CPLL = 0, CLK_SDMMC_PLL_SEL_GPLL, CLK_SDMMC_PLL_SEL_24M, CLK_SDMMC_PLL_SEL_USBPHY, CLK_SDMMC_PLL_SHIFT = 8, CLK_SDMMC_PLL_MASK = 0x3 << CLK_SDMMC_PLL_SHIFT, CLK_SDMMC_DIV_CON_SHIFT = 0, CLK_SDMMC_DIV_CON_MASK = 0xff << CLK_SDMMC_DIV_CON_SHIFT, /* CLKSEL_CON32 */ CLK_EMMC_PLL_SEL_CPLL = 0, CLK_EMMC_PLL_SEL_GPLL, CLK_EMMC_PLL_SEL_24M, CLK_EMMC_PLL_SEL_USBPHY, CLK_EMMC_PLL_SHIFT = 8, CLK_EMMC_PLL_MASK = 0x3 << CLK_EMMC_PLL_SHIFT, CLK_EMMC_DIV_CON_SHIFT = 0, CLK_EMMC_DIV_CON_MASK = 0xff << CLK_EMMC_DIV_CON_SHIFT, /* CLKSEL_CON34 */ CLK_I2C_PLL_SEL_CPLL = 0, CLK_I2C_PLL_SEL_GPLL, CLK_I2C_DIV_CON_MASK = 0x7f, CLK_I2C_PLL_SEL_MASK = 1, CLK_I2C1_PLL_SEL_SHIFT = 15, CLK_I2C1_DIV_CON_SHIFT = 8, CLK_I2C0_PLL_SEL_SHIFT = 7, CLK_I2C0_DIV_CON_SHIFT = 0, /* CLKSEL_CON35 */ CLK_I2C3_PLL_SEL_SHIFT = 15, CLK_I2C3_DIV_CON_SHIFT = 8, CLK_I2C2_PLL_SEL_SHIFT = 7, CLK_I2C2_DIV_CON_SHIFT = 0, }; #define VCO_MAX_KHZ (3200 * (MHz / KHz)) #define VCO_MIN_KHZ (800 * (MHz / KHz)) #define OUTPUT_MAX_KHZ (3200 * (MHz / KHz)) #define OUTPUT_MIN_KHZ (16 * (MHz / KHz)) /* * the div restructions of pll in integer mode, these are defined in * * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0 */ #define PLL_DIV_MIN 16 #define PLL_DIV_MAX 3200 /* * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): * Formulas also embedded within the Fractional PLL Verilog model: * If DSMPD = 1 (DSM is disabled, "integer mode") * FOUTVCO = FREF / REFDIV * FBDIV * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 * Where: * FOUTVCO = Fractional PLL non-divided output frequency * FOUTPOSTDIV = Fractional PLL divided output frequency * (output of second post divider) * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) * REFDIV = Fractional PLL input reference clock divider * FBDIV = Integer value programmed into feedback divide * */ static void rkclk_set_pll(struct rk3328_cru *cru, enum rk_clk_id clk_id, const struct pll_div *div) { u32 *pll_con; u32 mode_shift, mode_mask; pll_con = NULL; mode_shift = 0; switch (clk_id) { case CLK_ARM: pll_con = cru->apll_con; mode_shift = APLL_MODE_SHIFT; break; case CLK_DDR: pll_con = cru->dpll_con; mode_shift = DPLL_MODE_SHIFT; break; case CLK_CODEC: pll_con = cru->cpll_con; mode_shift = CPLL_MODE_SHIFT; break; case CLK_GENERAL: pll_con = cru->gpll_con; mode_shift = GPLL_MODE_SHIFT; break; case CLK_NEW: pll_con = cru->npll_con; mode_shift = NPLL_MODE_SHIFT; break; default: break; } mode_mask = 1 << mode_shift; /* All 8 PLLs have same VCO and output frequency range restrictions. */ u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv; u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2; debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, \ postdiv2=%d, vco=%u khz, output=%u khz\n", pll_con, div->fbdiv, div->refdiv, div->postdiv1, div->postdiv2, vco_khz, output_khz); assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ && output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ && div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX); /* * When power on or changing PLL setting, * we must force PLL into slow mode to ensure output stable clock. */ rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_SLOW << mode_shift); /* use integer mode */ rk_clrsetreg(&pll_con[1], PLL_DSMPD_MASK, PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); rk_clrsetreg(&pll_con[0], PLL_FBDIV_MASK | PLL_POSTDIV1_MASK, (div->fbdiv << PLL_FBDIV_SHIFT) | (div->postdiv1 << PLL_POSTDIV1_SHIFT)); rk_clrsetreg(&pll_con[1], PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, (div->postdiv2 << PLL_POSTDIV2_SHIFT) | (div->refdiv << PLL_REFDIV_SHIFT)); /* waiting for pll lock */ while (!(readl(&pll_con[1]) & (1 << PLL_LOCK_STATUS_SHIFT))) udelay(1); /* pll enter normal mode */ rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_NORM << mode_shift); } static void rkclk_init(struct rk3328_cru *cru) { u32 aclk_div; u32 hclk_div; u32 pclk_div; rk3328_configure_cpu(cru, APLL_600_MHZ); /* configure gpll cpll */ rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); /* configure perihp aclk, hclk, pclk */ aclk_div = GPLL_HZ / PERIHP_ACLK_HZ - 1; hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1; pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1; rk_clrsetreg(&cru->clksel_con[28], ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK, ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT | aclk_div << ACLK_PERIHP_DIV_CON_SHIFT); rk_clrsetreg(&cru->clksel_con[29], PCLK_PERIHP_DIV_CON_MASK | HCLK_PERIHP_DIV_CON_MASK, pclk_div << PCLK_PERIHP_DIV_CON_SHIFT | hclk_div << HCLK_PERIHP_DIV_CON_SHIFT); } void rk3328_configure_cpu(struct rk3328_cru *cru, enum apll_frequencies apll_freq) { u32 clk_core_div; u32 aclkm_div; u32 pclk_dbg_div; rkclk_set_pll(cru, CLK_ARM, apll_cfgs[apll_freq]); clk_core_div = APLL_HZ / CLK_CORE_HZ - 1; aclkm_div = APLL_HZ / ACLKM_CORE_HZ / (clk_core_div + 1) - 1; pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ / (clk_core_div + 1) - 1; rk_clrsetreg(&cru->clksel_con[0], CLK_CORE_PLL_SEL_MASK | CLK_CORE_DIV_MASK, CLK_CORE_PLL_SEL_APLL << CLK_CORE_PLL_SEL_SHIFT | clk_core_div << CLK_CORE_DIV_SHIFT); rk_clrsetreg(&cru->clksel_con[1], PCLK_DBG_DIV_MASK | ACLKM_CORE_DIV_MASK, pclk_dbg_div << PCLK_DBG_DIV_SHIFT | aclkm_div << ACLKM_CORE_DIV_SHIFT); } static ulong rk3328_i2c_get_clk(struct rk3328_cru *cru, ulong clk_id) { u32 div, con; switch (clk_id) { case SCLK_I2C0: con = readl(&cru->clksel_con[34]); div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; break; case SCLK_I2C1: con = readl(&cru->clksel_con[34]); div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; break; case SCLK_I2C2: con = readl(&cru->clksel_con[35]); div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; break; case SCLK_I2C3: con = readl(&cru->clksel_con[35]); div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; break; default: printf("do not support this i2c bus\n"); return -EINVAL; } return DIV_TO_RATE(GPLL_HZ, div); } static ulong rk3328_i2c_set_clk(struct rk3328_cru *cru, ulong clk_id, uint hz) { int src_clk_div; src_clk_div = GPLL_HZ / hz; assert(src_clk_div - 1 < 127); switch (clk_id) { case SCLK_I2C0: rk_clrsetreg(&cru->clksel_con[34], CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT, (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT); break; case SCLK_I2C1: rk_clrsetreg(&cru->clksel_con[34], CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT, (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT); break; case SCLK_I2C2: rk_clrsetreg(&cru->clksel_con[35], CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT, (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT); break; case SCLK_I2C3: rk_clrsetreg(&cru->clksel_con[35], CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT, (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT | CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT); break; default: printf("do not support this i2c bus\n"); return -EINVAL; } return DIV_TO_RATE(GPLL_HZ, src_clk_div); } static ulong rk3328_gmac2io_set_clk(struct rk3328_cru *cru, ulong rate) { struct rk3328_grf_regs *grf; ulong ret; grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); /* * The RGMII CLK can be derived either from an external "clkin" * or can be generated from internally by a divider from SCLK_MAC. */ if (readl(&grf->mac_con[1]) & BIT(10) && readl(&grf->soc_con[4]) & BIT(14)) { /* An external clock will always generate the right rate... */ ret = rate; } else { u32 con = readl(&cru->clksel_con[27]); ulong pll_rate; u8 div; if ((con >> GMAC2IO_PLL_SEL_SHIFT) & GMAC2IO_PLL_SEL_GPLL) pll_rate = GPLL_HZ; else pll_rate = CPLL_HZ; div = DIV_ROUND_UP(pll_rate, rate) - 1; if (div <= 0x1f) rk_clrsetreg(&cru->clksel_con[27], GMAC2IO_CLK_DIV_MASK, div << GMAC2IO_CLK_DIV_SHIFT); else debug("Unsupported div for gmac:%d\n", div); return DIV_TO_RATE(pll_rate, div); } return ret; } static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id) { u32 div, con, con_id; switch (clk_id) { case HCLK_SDMMC: case SCLK_SDMMC: con_id = 30; break; case HCLK_EMMC: case SCLK_EMMC: con_id = 32; break; default: return -EINVAL; } con = readl(&cru->clksel_con[con_id]); div = (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT; if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT == CLK_EMMC_PLL_SEL_24M) return DIV_TO_RATE(OSC_HZ, div) / 2; else return DIV_TO_RATE(GPLL_HZ, div) / 2; } static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru, ulong clk_id, ulong set_rate) { int src_clk_div; u32 con_id; switch (clk_id) { case HCLK_SDMMC: case SCLK_SDMMC: con_id = 30; break; case HCLK_EMMC: case SCLK_EMMC: con_id = 32; break; default: return -EINVAL; } /* Select clk_sdmmc/emmc source from GPLL by default */ /* mmc clock defaulg div 2 internal, need provide double in cru */ src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate); if (src_clk_div > 127) { /* use 24MHz source for 400KHz clock */ src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); rk_clrsetreg(&cru->clksel_con[con_id], CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); } else { rk_clrsetreg(&cru->clksel_con[con_id], CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); } return rk3328_mmc_get_clk(cru, clk_id); } static ulong rk3328_pwm_get_clk(struct rk3328_cru *cru) { u32 div, con; con = readl(&cru->clksel_con[24]); div = (con & CLK_PWM_DIV_CON_MASK) >> CLK_PWM_DIV_CON_SHIFT; return DIV_TO_RATE(GPLL_HZ, div); } static ulong rk3328_pwm_set_clk(struct rk3328_cru *cru, uint hz) { u32 div = GPLL_HZ / hz; rk_clrsetreg(&cru->clksel_con[24], CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, CLK_PWM_PLL_SEL_GPLL << CLK_PWM_PLL_SEL_SHIFT | (div - 1) << CLK_PWM_DIV_CON_SHIFT); return DIV_TO_RATE(GPLL_HZ, div); } static ulong rk3328_saradc_get_clk(struct rk3328_cru *cru) { u32 div, val; val = readl(&cru->clksel_con[23]); div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT, CLK_SARADC_DIV_CON_WIDTH); return DIV_TO_RATE(OSC_HZ, div); } static ulong rk3328_saradc_set_clk(struct rk3328_cru *cru, uint hz) { int src_clk_div; src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1; assert(src_clk_div < 128); rk_clrsetreg(&cru->clksel_con[23], CLK_SARADC_DIV_CON_MASK, src_clk_div << CLK_SARADC_DIV_CON_SHIFT); return rk3328_saradc_get_clk(cru); } static ulong rk3328_spi_get_clk(struct rk3328_cru *cru) { u32 div, val; val = readl(&cru->clksel_con[24]); div = (val & CLK_SPI_DIV_CON_MASK) >> CLK_SPI_DIV_CON_SHIFT; return DIV_TO_RATE(OSC_HZ, div); } static ulong rk3328_spi_set_clk(struct rk3328_cru *cru, uint hz) { u32 src_clk_div; src_clk_div = GPLL_HZ / hz; assert(src_clk_div < 128); rk_clrsetreg(&cru->clksel_con[24], CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, CLK_PWM_PLL_SEL_GPLL << CLK_PWM_PLL_SEL_SHIFT | (src_clk_div - 1) << CLK_PWM_DIV_CON_SHIFT); return rk3328_spi_get_clk(cru); } static ulong rk3328_clk_get_rate(struct clk *clk) { struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); ulong rate = 0; switch (clk->id) { case 0 ... 29: return 0; case HCLK_SDMMC: case HCLK_EMMC: case SCLK_SDMMC: case SCLK_EMMC: rate = rk3328_mmc_get_clk(priv->cru, clk->id); break; case SCLK_I2C0: case SCLK_I2C1: case SCLK_I2C2: case SCLK_I2C3: rate = rk3328_i2c_get_clk(priv->cru, clk->id); break; case SCLK_PWM: rate = rk3328_pwm_get_clk(priv->cru); break; case SCLK_SARADC: rate = rk3328_saradc_get_clk(priv->cru); break; case SCLK_SPI: rate = rk3328_spi_get_clk(priv->cru); break; default: return -ENOENT; } return rate; } static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate) { struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); ulong ret = 0; switch (clk->id) { case 0 ... 29: return 0; case HCLK_SDMMC: case HCLK_EMMC: case SCLK_SDMMC: case SCLK_EMMC: ret = rk3328_mmc_set_clk(priv->cru, clk->id, rate); break; case SCLK_I2C0: case SCLK_I2C1: case SCLK_I2C2: case SCLK_I2C3: ret = rk3328_i2c_set_clk(priv->cru, clk->id, rate); break; case SCLK_MAC2IO: ret = rk3328_gmac2io_set_clk(priv->cru, rate); break; case SCLK_PWM: ret = rk3328_pwm_set_clk(priv->cru, rate); break; case SCLK_SARADC: ret = rk3328_saradc_set_clk(priv->cru, rate); break; case SCLK_SPI: ret = rk3328_spi_set_clk(priv->cru, rate); break; case DCLK_LCDC: case SCLK_PDM: case SCLK_RTC32K: case SCLK_UART0: case SCLK_UART1: case SCLK_UART2: case SCLK_SDIO: case SCLK_TSP: case SCLK_WIFI: case ACLK_BUS_PRE: case HCLK_BUS_PRE: case PCLK_BUS_PRE: case ACLK_PERI_PRE: case HCLK_PERI: case PCLK_PERI: case ACLK_VIO_PRE: case HCLK_VIO_PRE: case ACLK_RGA_PRE: case SCLK_RGA: case ACLK_VOP_PRE: case ACLK_RKVDEC_PRE: case ACLK_RKVENC: case ACLK_VPU_PRE: case SCLK_VDEC_CABAC: case SCLK_VDEC_CORE: case SCLK_VENC_CORE: case SCLK_VENC_DSP: case SCLK_EFUSE: case PCLK_DDR: case ACLK_GMAC: case PCLK_GMAC: case SCLK_USB3OTG_SUSPEND: case USB480M: return 0; default: return -ENOENT; } return ret; } static int rk3328_gmac2io_set_parent(struct clk *clk, struct clk *parent) { struct rk3328_grf_regs *grf; const char *clock_output_name; int ret; grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); /* * If the requested parent is in the same clock-controller and the id * is SCLK_MAC2IO_SRC ("clk_mac2io_src"), switch to the internal clock. */ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO_SRC)) { debug("%s: switching RGMII to SCLK_MAC2IO_SRC\n", __func__); rk_clrreg(&grf->mac_con[1], BIT(10)); return 0; } /* * Otherwise, we need to check the clock-output-names of the * requested parent to see if the requested id is "gmac_clkin". */ ret = dev_read_string_index(parent->dev, "clock-output-names", parent->id, &clock_output_name); if (ret < 0) return -ENODATA; /* If this is "gmac_clkin", switch to the external clock input */ if (!strcmp(clock_output_name, "gmac_clkin")) { debug("%s: switching RGMII to CLKIN\n", __func__); rk_setreg(&grf->mac_con[1], BIT(10)); return 0; } return -EINVAL; } static int rk3328_gmac2io_ext_set_parent(struct clk *clk, struct clk *parent) { struct rk3328_grf_regs *grf; const char *clock_output_name; int ret; grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); /* * If the requested parent is in the same clock-controller and the id * is SCLK_MAC2IO ("clk_mac2io"), switch to the internal clock. */ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO)) { debug("%s: switching RGMII to SCLK_MAC2IO\n", __func__); rk_clrreg(&grf->soc_con[4], BIT(14)); return 0; } /* * Otherwise, we need to check the clock-output-names of the * requested parent to see if the requested id is "gmac_clkin". */ ret = dev_read_string_index(parent->dev, "clock-output-names", parent->id, &clock_output_name); if (ret < 0) return -ENODATA; /* If this is "gmac_clkin", switch to the external clock input */ if (!strcmp(clock_output_name, "gmac_clkin")) { debug("%s: switching RGMII to CLKIN\n", __func__); rk_setreg(&grf->soc_con[4], BIT(14)); return 0; } return -EINVAL; } static int rk3328_clk_set_parent(struct clk *clk, struct clk *parent) { switch (clk->id) { case SCLK_MAC2IO: return rk3328_gmac2io_set_parent(clk, parent); case SCLK_MAC2IO_EXT: return rk3328_gmac2io_ext_set_parent(clk, parent); case DCLK_LCDC: case USB480M: case SCLK_PDM: case SCLK_RTC32K: case SCLK_UART0: case SCLK_UART1: case SCLK_UART2: return 0; } debug("%s: unsupported clk %ld\n", __func__, clk->id); return -ENOENT; } static struct clk_ops rk3328_clk_ops = { .get_rate = rk3328_clk_get_rate, .set_rate = rk3328_clk_set_rate, .set_parent = rk3328_clk_set_parent, }; static int rk3328_clk_probe(struct udevice *dev) { struct rk3328_clk_priv *priv = dev_get_priv(dev); rkclk_init(priv->cru); return 0; } static int rk3328_clk_of_to_plat(struct udevice *dev) { struct rk3328_clk_priv *priv = dev_get_priv(dev); priv->cru = dev_read_addr_ptr(dev); return 0; } static int rk3328_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 rk3328_cru, glb_srst_fst_value); priv->glb_srst_snd_value = offsetof(struct rk3328_cru, glb_srst_snd_value); dev_set_priv(sys_child, priv); } #if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3328_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 12); if (ret) debug("Warning: software reset driver bind failed\n"); #endif return ret; } static const struct udevice_id rk3328_clk_ids[] = { { .compatible = "rockchip,rk3328-cru" }, { } }; U_BOOT_DRIVER(rockchip_rk3328_cru) = { .name = "rockchip_rk3328_cru", .id = UCLASS_CLK, .of_match = rk3328_clk_ids, .priv_auto = sizeof(struct rk3328_clk_priv), .of_to_plat = rk3328_clk_of_to_plat, .ops = &rk3328_clk_ops, .bind = rk3328_clk_bind, .probe = rk3328_clk_probe, };