diff --git a/arch/arm/include/asm/arch-rockchip/cru_px30.h b/arch/arm/include/asm/arch-rockchip/cru_px30.h new file mode 100644 index 0000000000..d27283f192 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/cru_px30.h @@ -0,0 +1,243 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _ASM_ARCH_CRU_px30_H +#define _ASM_ARCH_CRU_px30_H + +#include + +#define MHz 1000000 +#define OSC_HZ (24 * MHz) + +#define APLL_HZ (816 * MHz) +#define GPLL_HZ (1200 * MHz) +#define CPLL_HZ (594 * MHz) + +#define CORE_PERI_HZ 204000000 +#define CORE_ACLK_HZ 408000000 + +#define BUS_ACLK_HZ 148500000 +#define BUS_HCLK_HZ 148500000 +#define BUS_PCLK_HZ 74250000 + +#define PERI_ACLK_HZ 148500000 +#define PERI_HCLK_HZ 148500000 +#define PERI_PCLK_HZ 74250000 + +enum apll_frequencies { + APLL_816_MHZ, + APLL_600_MHZ, +}; + +/* Private data for the clock driver - used by rockchip_get_cru() */ +struct px30_clk_priv { + struct px30_cru *cru; + ulong rate; +}; + +struct px30_cru { + struct px30_pll { + unsigned int con0; + unsigned int con1; + unsigned int con2; + unsigned int con3; + unsigned int con4; + unsigned int reserved0[3]; + } pll[4]; + unsigned int reserved1[8]; + unsigned int mode; + unsigned int misc; + unsigned int reserved2[2]; + unsigned int glb_cnt_th; + unsigned int glb_rst_st; + unsigned int glb_srst_fst; + unsigned int glb_srst_snd; + unsigned int glb_rst_con; + unsigned int reserved3[7]; + unsigned int hwffc_con0; + unsigned int reserved4; + unsigned int hwffc_th; + unsigned int hwffc_intst; + unsigned int apll_con0_s; + unsigned int apll_con1_s; + unsigned int clksel_con0_s; + unsigned int reserved5; + unsigned int clksel_con[60]; + unsigned int reserved6[4]; + unsigned int clkgate_con[18]; + unsigned int reserved7[(0x280 - 0x244) / 4 - 1]; + unsigned int ssgtbl[32]; + unsigned int softrst_con[12]; + unsigned int reserved8[(0x380 - 0x32c) / 4 - 1]; + unsigned int sdmmc_con[2]; + unsigned int sdio_con[2]; + unsigned int emmc_con[2]; + unsigned int reserved9[(0x400 - 0x394) / 4 - 1]; + unsigned int autocs_con[8]; + unsigned int reserved10[(0xc000 - 0x41c) / 4 - 1]; + struct px30_pll gpll; + unsigned int pmu_mode; + unsigned int reserved11[7]; + unsigned int pmu_clksel_con[6]; + unsigned int pmu_clkgate_con[2]; + unsigned int reserved12[(0xc0c0 - 0xc05c) / 4 - 1]; + unsigned int pmu_autocs_con[2]; +}; +check_member(px30_cru, pmu_autocs_con[1], 0xc0c4); + +struct pll_div { + u32 refdiv; + u32 fbdiv; + u32 postdiv1; + u32 postdiv2; + u32 frac; +}; + +enum { + /* PLLCON0*/ + PLL_BP_SHIFT = 15, + PLL_POSTDIV1_SHIFT = 12, + PLL_POSTDIV1_MASK = 7 << PLL_POSTDIV1_SHIFT, + PLL_FBDIV_SHIFT = 0, + PLL_FBDIV_MASK = 0xfff, + + /* PLLCON1 */ + PLL_PDSEL_SHIFT = 15, + PLL_PD1_SHIFT = 14, + PLL_PD_SHIFT = 13, + PLL_PD_MASK = 1 << PLL_PD_SHIFT, + PLL_DSMPD_SHIFT = 12, + PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, + PLL_LOCK_STATUS_SHIFT = 10, + PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, + PLL_POSTDIV2_SHIFT = 6, + PLL_POSTDIV2_MASK = 7 << PLL_POSTDIV2_SHIFT, + PLL_REFDIV_SHIFT = 0, + PLL_REFDIV_MASK = 0x3f, + + /* PLLCON2 */ + PLL_FOUT4PHASEPD_SHIFT = 27, + PLL_FOUTVCOPD_SHIFT = 26, + PLL_FOUTPOSTDIVPD_SHIFT = 25, + PLL_DACPD_SHIFT = 24, + PLL_FRAC_DIV = 0xffffff, + + /* CRU_MODE */ + PLLMUX_FROM_XIN24M = 0, + PLLMUX_FROM_PLL, + PLLMUX_FROM_RTC32K, + USBPHY480M_MODE_SHIFT = 8, + USBPHY480M_MODE_MASK = 3 << USBPHY480M_MODE_SHIFT, + NPLL_MODE_SHIFT = 6, + NPLL_MODE_MASK = 3 << NPLL_MODE_SHIFT, + DPLL_MODE_SHIFT = 4, + DPLL_MODE_MASK = 3 << DPLL_MODE_SHIFT, + CPLL_MODE_SHIFT = 2, + CPLL_MODE_MASK = 3 << CPLL_MODE_SHIFT, + APLL_MODE_SHIFT = 0, + APLL_MODE_MASK = 3 << APLL_MODE_SHIFT, + + /* CRU_CLK_SEL0_CON */ + CORE_ACLK_DIV_SHIFT = 12, + CORE_ACLK_DIV_MASK = 0x07 << CORE_ACLK_DIV_SHIFT, + CORE_DBG_DIV_SHIFT = 8, + CORE_DBG_DIV_MASK = 0x03 << CORE_DBG_DIV_SHIFT, + CORE_CLK_PLL_SEL_SHIFT = 7, + CORE_CLK_PLL_SEL_MASK = 1 << CORE_CLK_PLL_SEL_SHIFT, + CORE_CLK_PLL_SEL_APLL = 0, + CORE_CLK_PLL_SEL_GPLL, + CORE_DIV_CON_SHIFT = 0, + CORE_DIV_CON_MASK = 0x0f << CORE_DIV_CON_SHIFT, + + /* CRU_CLK_SEL14_CON */ + PERI_PLL_SEL_SHIFT =15, + PERI_PLL_SEL_MASK = 3 << PERI_PLL_SEL_SHIFT, + PERI_PLL_GPLL = 0, + PERI_PLL_CPLL, + PERI_HCLK_DIV_SHIFT = 8, + PERI_HCLK_DIV_MASK = 0x1f << PERI_HCLK_DIV_SHIFT, + PERI_ACLK_DIV_SHIFT = 0, + PERI_ACLK_DIV_MASK = 0x1f << PERI_ACLK_DIV_SHIFT, + + /* CRU_CLKSEL20_CON */ + EMMC_PLL_SHIFT = 14, + EMMC_PLL_MASK = 3 << EMMC_PLL_SHIFT, + EMMC_SEL_GPLL = 0, + EMMC_SEL_CPLL, + EMMC_SEL_NPLL, + EMMC_SEL_24M, + EMMC_DIV_SHIFT = 0, + EMMC_DIV_MASK = 0xff << EMMC_DIV_SHIFT, + + /* CRU_CLKSEL21_CON */ + EMMC_CLK_SEL_SHIFT = 15, + EMMC_CLK_SEL_MASK = 1 << EMMC_CLK_SEL_SHIFT, + EMMC_CLK_SEL_EMMC = 0, + EMMC_CLK_SEL_EMMC_DIV50, + EMMC_DIV50_SHIFT = 0, + EMMC_DIV50_MASK = 0xff << EMMC_DIV_SHIFT, + + /* CRU_CLKSEL22_CON */ + GMAC_PLL_SEL_SHIFT = 14, + GMAC_PLL_SEL_MASK = 3 << GMAC_PLL_SEL_SHIFT, + GMAC_PLL_SEL_GPLL = 0, + GMAC_PLL_SEL_CPLL, + GMAC_PLL_SEL_NPLL, + CLK_GMAC_DIV_SHIFT = 8, + CLK_GMAC_DIV_MASK = 0x1f << CLK_GMAC_DIV_SHIFT, + SFC_PLL_SEL_SHIFT = 7, + SFC_PLL_SEL_MASK = 1 << SFC_PLL_SEL_SHIFT, + SFC_DIV_CON_SHIFT = 0, + SFC_DIV_CON_MASK = 0x7f, + + /* CRU_CLK_SEL23_CON */ + BUS_PLL_SEL_SHIFT =15, + BUS_PLL_SEL_MASK = 3 << BUS_PLL_SEL_SHIFT, + BUS_PLL_SEL_GPLL = 0, + BUS_PLL_SEL_CPLL, + BUS_ACLK_DIV_SHIFT = 8, + BUS_ACLK_DIV_MASK = 0x1f << BUS_ACLK_DIV_SHIFT, + RMII_CLK_SEL_SHIFT = 7, + RMII_CLK_SEL_MASK = 1 << RMII_CLK_SEL_SHIFT, + RMII_CLK_SEL_10M = 0, + RMII_CLK_SEL_100M, + RMII_EXTCLK_SEL_SHIFT = 6, + RMII_EXTCLK_SEL_MASK = 1 << RMII_EXTCLK_SEL_SHIFT, + RMII_EXTCLK_SEL_INT = 0, + RMII_EXTCLK_SEL_EXT, + PCLK_GMAC_DIV_SHIFT = 0, + PCLK_GMAC_DIV_MASK = 0x0f << PCLK_GMAC_DIV_SHIFT, + + /* CRU_CLK_SEL24_CON */ + BUS_PCLK_DIV_SHIFT = 8, + BUS_PCLK_DIV_MASK = 3 << BUS_PCLK_DIV_SHIFT, + BUS_HCLK_DIV_SHIFT = 0, + BUS_HCLK_DIV_MASK = 0x1f << BUS_HCLK_DIV_SHIFT, + + /* CRU_CLK_SEL24_CON */ + UART2_PLL_SEL_SHIFT = 14, + UART2_PLL_SEL_MASK = 3 << UART2_PLL_SEL_SHIFT, + UART2_PLL_SEL_GPLL = 0, + UART2_PLL_SEL_24M, + UART2_PLL_SEL_480M, + UART2_PLL_SEL_NPLL, + UART2_DIV_CON_SHIFT = 0, + UART2_DIV_CON_MASK = 0x1f << UART2_DIV_CON_SHIFT, + + /* CRU_CLK_SEL25_CON */ + UART2_CLK_SEL_SHIFT = 14, + UART2_CLK_SEL_MASK = 3 << UART2_PLL_SEL_SHIFT, + UART2_CLK_SEL_UART2 = 0, + UART2_CLK_SEL_UART2_NP5, + UART2_CLK_SEL_UART2_FRAC, + UART2_DIVNP5_SHIFT = 0, + UART2_DIVNP5_MASK = 0x1f << UART2_DIVNP5_SHIFT, + + /* CRU_PMU_MODE */ + GPLL_MODE_SHIFT = 0, + GPLL_MODE_MASK = 3 << GPLL_MODE_SHIFT, + +}; +#endif diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index c662de1230..62d318cade 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -4,6 +4,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_ROCKCHIP_PX30) += clk_px30.o obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o obj-$(CONFIG_ROCKCHIP_RK3066) += clk_rk3066.o obj-$(CONFIG_ROCKCHIP_RK3128) += clk_rk3128.o diff --git a/drivers/clk/rockchip/clk_px30.c b/drivers/clk/rockchip/clk_px30.c new file mode 100644 index 0000000000..89b18a2f8e --- /dev/null +++ b/drivers/clk/rockchip/clk_px30.c @@ -0,0 +1,577 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#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 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 apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); +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, +}; + +/* + * 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(void *pll_base, const struct pll_div *div) +{ + struct px30_pll *pll = (struct px30_pll *)pll_base; + /* All PLLs have same VCO and output frequency range restrictions. */ + uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; + uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; + + debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n", + pll, div->fbdiv, div->refdiv, div->postdiv1, + div->postdiv2, vco_hz, output_hz); + assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && + output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); + + /* use integer mode */ + rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); + /* Power down */ + rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT); + + rk_clrsetreg(&pll->con0, + PLL_POSTDIV1_MASK | PLL_FBDIV_MASK, + (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv); + rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, + (div->postdiv2 << PLL_POSTDIV2_SHIFT | + div->refdiv << PLL_REFDIV_SHIFT)); + + /* Power Up */ + rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT); + + /* waiting for pll lock */ + while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)) + udelay(1); + + return; +} + +static void rkclk_init(struct px30_cru *cru) +{ + u32 aclk_div; + u32 hclk_div; + u32 pclk_div; + + rk_clrsetreg(&cru->mode, APLL_MODE_MASK, + PLLMUX_FROM_XIN24M << APLL_MODE_SHIFT); + rk_clrsetreg(&cru->pmu_mode, GPLL_MODE_MASK, + PLLMUX_FROM_XIN24M << GPLL_MODE_SHIFT); + + /* init pll */ + rkclk_set_pll(&cru->pll[0] , &apll_816_cfg); + rkclk_set_pll(&cru->gpll, &gpll_init_cfg); + + /* + * select apll as cpu/core clock pll source and + * set up dependent divisors for PERI and ACLK clocks. + * core hz : apll = 1:1 + */ + aclk_div = APLL_HZ / CORE_ACLK_HZ - 1; + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK, + aclk_div << CORE_ACLK_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + + /* + * select gpll as pd_bus bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / BUS_ACLK_HZ - 1; + hclk_div = GPLL_HZ / BUS_HCLK_HZ - 1; + pclk_div = BUS_ACLK_HZ / BUS_PCLK_HZ - 1; + + rk_clrsetreg(&cru->clksel_con[23], + BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, + BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT | + aclk_div << BUS_ACLK_DIV_SHIFT); + + rk_clrsetreg(&cru->clksel_con[24], + BUS_PCLK_DIV_MASK | BUS_HCLK_DIV_MASK, + pclk_div << BUS_PCLK_DIV_SHIFT | + hclk_div << BUS_HCLK_DIV_SHIFT); + + /* + * select gpll as pd_peri bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; + hclk_div = GPLL_HZ / PERI_HCLK_HZ - 1; + + rk_clrsetreg(&cru->clksel_con[14], + PERI_PLL_SEL_MASK | + PERI_HCLK_DIV_MASK | PERI_ACLK_DIV_MASK, + PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | + hclk_div << PERI_HCLK_DIV_SHIFT | + aclk_div << PERI_ACLK_DIV_SHIFT); + + rk_clrsetreg(&cru->mode, APLL_MODE_MASK, + PLLMUX_FROM_PLL << APLL_MODE_SHIFT); + rk_clrsetreg(&cru->pmu_mode, GPLL_MODE_MASK, + PLLMUX_FROM_PLL << GPLL_MODE_SHIFT); +} + +static ulong px30_i2c_get_clk(struct px30_cru *cru, ulong clk_id) +{ + u32 div, con; + + switch (clk_id) { + case SCLK_I2C0: + break; + case SCLK_I2C1: + break; + case SCLK_I2C2: + break; + case SCLK_I2C3: + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong px30_i2c_set_clk(struct px30_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: + break; + case SCLK_I2C1: + break; + case SCLK_I2C2: + break; + case SCLK_I2C3: + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(GPLL_HZ, src_clk_div); +} + +static ulong px30_mmc_get_clk(struct px30_cru *cru, uint clk_id) +{ + u32 div, con, con_id; + + switch (clk_id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 16; + break; + case HCLK_EMMC: + case SCLK_EMMC: + con_id = 20; + break; + default: + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; + + if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT + == EMMC_SEL_24M) + return DIV_TO_RATE(OSC_HZ, div) / 2; + else + return DIV_TO_RATE(GPLL_HZ, div) / 2; + +} + +static ulong px30_mmc_set_clk(struct px30_cru *cru, + ulong clk_id, ulong set_rate) +{ + int src_clk_div; + u32 con_id; + + printf("%s %d %d\n", __func__, clk_id, set_rate); + switch (clk_id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 16; + break; + case HCLK_EMMC: + case SCLK_EMMC: + con_id = 20; + 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], + EMMC_PLL_MASK | EMMC_DIV_MASK, + EMMC_SEL_24M << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK, + EMMC_SEL_GPLL << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } + rk_clrsetreg(&cru->clksel_con[con_id +1], EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC); + + return px30_mmc_get_clk(cru, clk_id); +} + +static ulong px30_pwm_get_clk(struct px30_cru *cru) +{ + u32 div, con; + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong px30_pwm_set_clk(struct px30_cru *cru, uint hz) +{ + u32 div = GPLL_HZ / hz; + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong px30_saradc_get_clk(struct px30_cru *cru) +{ + u32 div, val; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong px30_saradc_set_clk(struct px30_cru *cru, uint hz) +{ + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1; + + return px30_saradc_get_clk(cru); +} + +static ulong px30_clk_get_rate(struct clk *clk) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + switch (clk->id) { + case 0 ... 15: + return 0; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + rate = px30_mmc_get_clk(priv->cru, clk->id); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + rate = px30_i2c_get_clk(priv->cru, clk->id); + break; + case SCLK_PWM0: + rate = px30_pwm_get_clk(priv->cru); + break; + case SCLK_SARADC: + rate = px30_saradc_get_clk(priv->cru); + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong px30_clk_set_rate(struct clk *clk, ulong rate) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + printf("%s %d %d\n", __func__, clk->id, rate); + switch (clk->id) { + case 0 ... 15: + return 0; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + ret = px30_mmc_set_clk(priv->cru, clk->id, rate); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + ret = px30_i2c_set_clk(priv->cru, clk->id, rate); + break; + case SCLK_PWM0: + ret = px30_pwm_set_clk(priv->cru, rate); + break; + case SCLK_SARADC: + ret = px30_saradc_set_clk(priv->cru, rate); + break; + default: + return -ENOENT; + } + + printf("%s %d\n", __func__, ret); + return ret; +} + +#define ROCKCHIP_MMC_DELAY_SEL BIT(10) +#define ROCKCHIP_MMC_DEGREE_MASK 0x3 +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) + +#define PSECS_PER_SEC 1000000000000LL +/* + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. + */ +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 + +int rockchip_mmc_get_phase(struct clk *clk) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + struct px30_cru *cru = priv->cru; + u32 raw_value, delay_num; + u16 degrees = 0; + ulong rate; + + rate = px30_clk_get_rate(clk); + + if (rate < 0) + return rate; + + if (clk->id == SCLK_EMMC_SAMPLE) + raw_value = readl(&cru->emmc_con[1]); + else + raw_value = readl(&cru->sdmmc_con[1]); + + degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; + + if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { + /* degrees/delaynum * 10000 */ + unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * + 36 * (rate / 1000000); + + delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); + delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; + degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000); + } + + return degrees % 360; +} + +int rockchip_mmc_set_phase(struct clk *clk, u32 degrees) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + struct px30_cru *cru = priv->cru; + u8 nineties, remainder, delay_num; + u32 raw_value, delay; + ulong rate; + + rate = px30_clk_get_rate(clk); + + if (rate < 0) + return rate; + + nineties = degrees / 90; + remainder = (degrees % 90); + + /* + * Convert to delay; do a little extra work to make sure we + * don't overflow 32-bit / 64-bit numbers. + */ + delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */ + delay *= remainder; + delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 * + (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10)); + + delay_num = (u8)min_t(u32, delay, 255); + + raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; + raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; + raw_value |= nineties; + + if (clk->id == SCLK_EMMC_SAMPLE) + writel(raw_value | 0xffff0000, &cru->emmc_con[1]); + else + writel(raw_value | 0xffff0000, &cru->sdmmc_con[1]); + + debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n", + degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk)); + + return 0; +} + +static int px30_clk_get_phase(struct clk *clk) +{ + int ret; + + switch (clk->id) { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + ret = rockchip_mmc_get_phase(clk); + break; + default: + return -ENOENT; + } + + return ret; +} + +static int px30_clk_set_phase(struct clk *clk, int degrees) +{ + int ret; + + switch (clk->id) { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + ret = rockchip_mmc_set_phase(clk, degrees); + break; + default: + return -ENOENT; + } + + return ret; +} + +static struct clk_ops px30_clk_ops = { + .get_rate = px30_clk_get_rate, + .set_rate = px30_clk_set_rate, + .get_phase = px30_clk_get_phase, + .set_phase = px30_clk_set_phase, +}; + +static int px30_clk_probe(struct udevice *dev) +{ + struct px30_clk_priv *priv = dev_get_priv(dev); + + rkclk_init(priv->cru); + + return 0; +} + +static int px30_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct px30_clk_priv *priv = dev_get_priv(dev); + + priv->cru = (struct px30_cru *)devfdt_get_addr(dev); + + return 0; +} + +static int px30_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child, *sf_child; + struct sysreset_reg *priv; + struct softreset_reg *sf_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 px30_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct px30_cru, + glb_srst_snd); + sys_child->priv = priv; + } + + ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset", + dev_ofnode(dev), &sf_child); + if (ret) { + debug("Warning: No rockchip reset driver: ret=%d\n", ret); + } else { + sf_priv = malloc(sizeof(struct softreset_reg)); + sf_priv->sf_reset_offset = offsetof(struct px30_cru, + softrst_con[0]); + sf_priv->sf_reset_num = 12; + sf_child->priv = sf_priv; + } + + return 0; +} + +static const struct udevice_id px30_clk_ids[] = { + { .compatible = "rockchip,px30-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_px30_cru) = { + .name = "rockchip_px30_cru", + .id = UCLASS_CLK, + .of_match = px30_clk_ids, + .priv_auto_alloc_size = sizeof(struct px30_clk_priv), + .ofdata_to_platdata = px30_clk_ofdata_to_platdata, + .ops = &px30_clk_ops, + .bind = px30_clk_bind, + .probe = px30_clk_probe, +};