From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4861EC433F5 for ; Thu, 20 Jan 2022 00:31:27 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 516468385C; Thu, 20 Jan 2022 01:31:25 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="C7k9x7SM"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 001E18385C; Thu, 20 Jan 2022 01:31:23 +0100 (CET) Received: from mail-qk1-x732.google.com (mail-qk1-x732.google.com [IPv6:2607:f8b0:4864:20::732]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 3B58083868 for ; Thu, 20 Jan 2022 01:31:19 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x732.google.com with SMTP id 193so4617598qkh.13 for ; Wed, 19 Jan 2022 16:31:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=AqtvDBNes14EIYfByk0lA5HG3kgwoEWMwe6lXXIU4WA=; b=C7k9x7SMqwfBOrLJ8jTbFaPZRvrnVqAP2actBwIKGjiYasd/aT9ptJ46jS+o/H0AQ4 U1CXXkD+mVh/NitsUwjFHlsApAKB0Q3Dtx4embvO79c+/eaC+Q8tq+RZ+4YxEUGN0il9 e67D8JC6z3NSQyML4Hl2MQ+SoI9F8R8UogW7kVV1S4A+QT/rZbTAUewOBN5DLLcqVsDn 6e0qansoqytQhM8btUB09nbwCJ9ZNEf3EMoxrul1qioLXWn1S5b6kQVhrjz0tiXICV6H GfS8lg2UPkcki6CG7hnO+K43OaIMREjcT/riY06wQuKjZUYj4LQ/BHvlTuVrWh18ubDz zSKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=AqtvDBNes14EIYfByk0lA5HG3kgwoEWMwe6lXXIU4WA=; b=COJm7lZzq5w7R+c/4LaGXmwZe0PUt7boEXPf+z98sfjNhDe0P+AAbkqGO/9944U1uJ Tz9YKlwBh/98vXElODLYmoFRbuabRGTjBt98jVCqjsMKaHmvVwsRx/+yYUTaKJeFN6oj bRIKAQl+RJvAM4tGCe5E4zgRncHIXK0iijIJO3YqvoX+nHRuWJMSCn6T8oSv9vTRmgCv H2+3MWnNpY1iO/VHUlPeM5MOd5rVE0t8CgCsyil1A/6P6W0f4O5NGC6OzhTJt35x0gkb BhO8Z5VZeDM4aECIlL5TLgSKvgJBj3n9UVVtcubRc9bBKpuuMA2K9WIJBqkNNTJ9wKcV uhvQ== X-Gm-Message-State: AOAM531eS37Zyb0Xxbdpte6fnlmU57LP8Bw8bX3aFnNtAP5whrjzus7s wulh6YKuqiXfc+rbnJRtcUcNPcAyO4A= X-Google-Smtp-Source: ABdhPJxpYKUzRZJvMuv/t4ZMBo+af7IwkA1S767mqrDb6m7Uoa/tfVSl0fvQdZz3n1VHbzF/SEUMiA== X-Received: by 2002:a05:620a:4550:: with SMTP id u16mr15795545qkp.660.1642638677743; Wed, 19 Jan 2022 16:31:17 -0800 (PST) Received: from [192.168.1.201] (pool-108-18-137-133.washdc.fios.verizon.net. [108.18.137.133]) by smtp.googlemail.com with ESMTPSA id h3sm695563qkn.123.2022.01.19.16.31.17 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 19 Jan 2022 16:31:17 -0800 (PST) Subject: Re: [PATCH v8 03/15] rockchip: rk3066: add clock driver for rk3066 soc To: Johan Jonker , kever.yang@rock-chips.com Cc: sjg@chromium.org, philipp.tomsich@vrull.eu, lukma@denx.de, u-boot@lists.denx.de References: <20220118003703.10678-1-jbx6244@gmail.com> <20220118003703.10678-4-jbx6244@gmail.com> From: Sean Anderson Message-ID: <37690820-7e80-c2bb-dc53-e02bd14b3404@gmail.com> Date: Wed, 19 Jan 2022 19:31:16 -0500 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.12.0 MIME-Version: 1.0 In-Reply-To: <20220118003703.10678-4-jbx6244@gmail.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: quoted-printable X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean On 1/17/22 7:36 PM, Johan Jonker wrote: > From: Pawe=C5=82 Jarosz >=20 > Add the clock driver for the rk3066 platform. >=20 > Derived from the rk3288 and rk3188 driver it > supports only a bare minimum to bring up the system > to reduce the TPL size for: > SDRAM clock configuration. > The boot devices NAND, EMMC, SDMMC, SPI. > A UART for the debug messages (fixed) at 115200n8. > A SARADC for the recovery button. > A TIMER for the delays (fixed). >=20 > There's support for two possible frequencies, > the safe 600MHz which will work with default pmic settings and > will be set to get away from the 24MHz default and > the maximum of 1.416Ghz, which boards can set if they > were able to get pmic support for it. >=20 > After the clock tree is set during the TPL probe > there's no parent update support. >=20 > In OF_REAL mode the drivers ns16550.c and dw-apb-timer.c > obtain the (fixed) clk_get_rate from the clock driver > instead of platdata. >=20 > The rk3066 cru node has a number of assigned-clocks properties > that call the .set_rate() function. Add them to the list so that > they return a 0 instead of -ENOENT. >=20 > Signed-off-by: Pawe=C5=82 Jarosz > Signed-off-by: Johan Jonker > --- >=20 > Changed V8: > add SCLK_TIMER[0..2] > add SCLK_UART[0..3] > fix assigned-clocks > use GENMASK, __bf_shf and REG defines > fix clk defines > add includes > fix bit position CRU_CLKSEL0_CON rk3066 vs rk3188 > fix comments > rename PLL_MODE defines > use dev_bind > use dev_dbg > use a different variable name >=20 > Changed V7: > changed function prefix > changed #if where possible > restyle U_BOOT_DRIVER structure > --- > .../include/asm/arch-rockchip/cru_rk3066.h | 157 ++++ > drivers/clk/rockchip/Makefile | 1 + > drivers/clk/rockchip/clk_rk3066.c | 717 +++++++++++++++++= + > 3 files changed, 875 insertions(+) > create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3066.h > create mode 100644 drivers/clk/rockchip/clk_rk3066.c >=20 > diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3066.h b/arch/arm= /include/asm/arch-rockchip/cru_rk3066.h > new file mode 100644 > index 00000000..45937736 > --- /dev/null > +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3066.h > @@ -0,0 +1,157 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * (C) Copyright 2021 Pawe=C5=82 Jarosz > + */ > + > +#ifndef _ASM_ARCH_CRU_RK3066_H > +#define _ASM_ARCH_CRU_RK3066_H > + > +#include > +#include > + > +#define REG(name, h, l) \ > + name##_MASK =3D GENMASK(h, l), \ > + name##_SHIFT =3D __bf_shf(name##_MASK) > + > +#define OSC_HZ (24 * 1000 * 1000) > + > +#define APLL_HZ (1416 * 1000000) > +#define APLL_SAFE_HZ (600 * 1000000) > +#define GPLL_HZ (594 * 1000000) > +#define CPLL_HZ (384 * 1000000) > + > +/* The SRAM is clocked off aclk_cpu, so we want to max it out for boot= speed */ > +#define CPU_ACLK_HZ 297000000 > +#define CPU_HCLK_HZ 148500000 > +#define CPU_PCLK_HZ 74250000 > +#define CPU_H2P_HZ 74250000 > + > +#define PERI_ACLK_HZ 148500000 > +#define PERI_HCLK_HZ 148500000 > +#define PERI_PCLK_HZ 74250000 > + > +/* Private data for the clock driver - used by rockchip_get_cru() */ > +struct rk3066_clk_priv { > + struct rk3066_grf *grf; > + struct rk3066_cru *cru; > + ulong rate; > + bool has_bwadj; > +}; > + > +struct rk3066_cru { > + struct rk3066_pll { > + u32 con0; > + u32 con1; > + u32 con2; > + u32 con3; > + } pll[4]; > + u32 cru_mode_con; > + u32 cru_clksel_con[35]; > + u32 cru_clkgate_con[10]; > + u32 reserved1[2]; > + u32 cru_glb_srst_fst_value; > + u32 cru_glb_srst_snd_value; > + u32 reserved2[2]; > + u32 cru_softrst_con[9]; > + u32 cru_misc_con; > + u32 reserved3[2]; > + u32 cru_glb_cnt_th; > +}; > + > +check_member(rk3066_cru, cru_glb_cnt_th, 0x0140); > + > +/* CRU_CLKSEL0_CON */ > +enum { > + REG(CPU_ACLK_PLL, 8, 8), > + CPU_ACLK_PLL_SELECT_APLL =3D 0, > + CPU_ACLK_PLL_SELECT_GPLL, > + > + REG(CORE_PERI_DIV, 7, 6), > + > + REG(A9_CORE_DIV, 4, 0), > +}; > + > +/* CRU_CLKSEL1_CON */ > +enum { > + REG(AHB2APB_DIV, 15, 14), > + > + REG(CPU_PCLK_DIV, 13, 12), > + > + REG(CPU_HCLK_DIV, 9, 8), > + > + REG(CPU_ACLK_DIV, 2, 0), > +}; > + > +/* CRU_CLKSEL10_CON */ > +enum { > + REG(PERI_SEL_PLL, 15, 15), > + PERI_SEL_CPLL =3D 0, > + PERI_SEL_GPLL, > + > + REG(PERI_PCLK_DIV, 13, 12), > + > + REG(PERI_HCLK_DIV, 9, 8), > + > + REG(PERI_ACLK_DIV, 4, 0), > +}; > + > +/* CRU_CLKSEL11_CON */ > +enum { > + REG(MMC0_DIV, 5, 0), > +}; > + > +/* CRU_CLKSEL12_CON */ > +enum { > + REG(UART_PLL, 15, 15), > + UART_PLL_SELECT_GENERAL =3D 0, > + UART_PLL_SELECT_CODEC, > + > + REG(EMMC_DIV, 13, 8), > + > + REG(SDIO_DIV, 5, 0), > +}; > + > +/* CRU_CLKSEL24_CON */ > +enum { > + REG(SARADC_DIV, 15, 8), > +}; > + > +/* CRU_CLKSEL25_CON */ > +enum { > + REG(SPI1_DIV, 14, 8), > + > + REG(SPI0_DIV, 6, 0), > +}; > + > +/* CRU_CLKSEL34_CON */ > +enum { > + REG(TSADC_DIV, 15, 0), > +}; > + > +/* CRU_MODE_CON */ > +enum { > + REG(GPLL_MODE, 13, 12), > + > + REG(CPLL_MODE, 9, 8), > + > + REG(DPLL_MODE, 5, 4), > + > + REG(APLL_MODE, 1, 0), > + PLL_MODE_SLOW =3D 0, > + PLL_MODE_NORMAL, > + PLL_MODE_DEEP, > +}; > + > +/* CRU_APLL_CON0 */ > +enum { > + REG(CLKR, 13, 8), > + > + REG(CLKOD, 3, 0), > +}; > + > +/* CRU_APLL_CON1 */ > +enum { > + REG(CLKF, 12, 0), > +}; > + > +#endif > diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makef= ile > index 913f611a..a72d8fe5 100644 > --- a/drivers/clk/rockchip/Makefile > +++ b/drivers/clk/rockchip/Makefile > @@ -6,6 +6,7 @@ > obj-y +=3D clk_pll.o > obj-$(CONFIG_ROCKCHIP_PX30) +=3D clk_px30.o > obj-$(CONFIG_ROCKCHIP_RK3036) +=3D clk_rk3036.o > +obj-$(CONFIG_ROCKCHIP_RK3066) +=3D clk_rk3066.o > obj-$(CONFIG_ROCKCHIP_RK3128) +=3D clk_rk3128.o > obj-$(CONFIG_ROCKCHIP_RK3188) +=3D clk_rk3188.o > obj-$(CONFIG_ROCKCHIP_RK322X) +=3D clk_rk322x.o > diff --git a/drivers/clk/rockchip/clk_rk3066.c b/drivers/clk/rockchip/c= lk_rk3066.c > new file mode 100644 > index 00000000..39c1c157 > --- /dev/null > +++ b/drivers/clk/rockchip/clk_rk3066.c > @@ -0,0 +1,717 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * (C) Copyright 2015 Google, Inc > + * (C) Copyright 2016 Heiko Stuebner > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +struct rk3066_clk_plat { > +#if CONFIG_IS_ENABLED(OF_PLATDATA) > + struct dtd_rockchip_rk3066a_cru dtd; > +#endif > +}; > + > +struct pll_div { > + u32 nr; > + u32 nf; > + u32 no; > +}; > + > +enum { > + VCO_MAX_HZ =3D 1416U * 1000000, > + VCO_MIN_HZ =3D 300 * 1000000, > + OUTPUT_MAX_HZ =3D 1416U * 1000000, > + OUTPUT_MIN_HZ =3D 30 * 1000000, > + FREF_MAX_HZ =3D 1416U * 1000000, > + FREF_MIN_HZ =3D 30 * 1000, > +}; > + > +enum { > + /* PLL CON0 */ > + PLL_OD_MASK =3D GENMASK(3, 0), > + > + /* PLL CON1 */ > + PLL_NF_MASK =3D GENMASK(12, 0), > + > + /* PLL CON2 */ > + PLL_BWADJ_MASK =3D GENMASK(11, 0), > + > + /* PLL CON3 */ > + PLL_RESET_SHIFT =3D 5, > + > + /* GRF_SOC_STATUS0 */ > + SOCSTS_DPLL_LOCK =3D BIT(4), > + SOCSTS_APLL_LOCK =3D BIT(5), > + SOCSTS_CPLL_LOCK =3D BIT(6), > + SOCSTS_GPLL_LOCK =3D BIT(7), > +}; > + > +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) > + > +#define PLL_DIVISORS(hz, _nr, _no) {\ > + .nr =3D _nr, .nf =3D (u32)((u64)hz * _nr * _no / OSC_HZ), .no =3D _no= };\ > + _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\ > + (_nr * _no) =3D=3D hz, #hz "Hz cannot be hit with PLL "\ > + "divisors on line " __stringify(__LINE__)) > + > +/* Keep divisors as low as possible to reduce jitter and power usage. = */ > +static const struct pll_div gpll_init_cfg =3D PLL_DIVISORS(GPLL_HZ, 2,= 2); > +static const struct pll_div cpll_init_cfg =3D PLL_DIVISORS(CPLL_HZ, 1,= 2); > + > +static int rk3066_clk_set_pll(struct rk3066_cru *cru, enum rk_clk_id c= lk_id, > + const struct pll_div *div) > +{ > + int pll_id =3D rk_pll_id(clk_id); > + struct rk3066_pll *pll =3D &cru->pll[pll_id]; > + /* All PLLs have the same VCO and output frequency range restrictions= =2E */ > + uint vco_hz =3D OSC_HZ / 1000 * div->nf / div->nr * 1000; > + uint output_hz =3D vco_hz / div->no; > + > + debug("%s: PLL at %x: nf=3D%d, nr=3D%d, no=3D%d, vco=3D%u Hz, output=3D= %u Hz\n", __func__, > + (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz); > + assert(vco_hz >=3D VCO_MIN_HZ && vco_hz <=3D VCO_MAX_HZ && > + output_hz >=3D OUTPUT_MIN_HZ && output_hz <=3D OUTPUT_MAX_HZ &= & > + (div->no =3D=3D 1 || !(div->no % 2))); > + > + /* Enter reset. */ > + rk_setreg(&pll->con3, BIT(PLL_RESET_SHIFT)); > + > + rk_clrsetreg(&pll->con0, > + CLKR_MASK | PLL_OD_MASK, > + ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1)); > + rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1); > + > + rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); > + > + /* Exit reset. */ > + rk_clrreg(&pll->con3, BIT(PLL_RESET_SHIFT)); > + > + return 0; > +} > + > +static int rk3066_clk_configure_ddr(struct rk3066_cru *cru, struct rk3= 066_grf *grf, > + unsigned int hz) > +{ > + static const struct pll_div dpll_cfg[] =3D { > + {.nf =3D 25, .nr =3D 2, .no =3D 1}, > + {.nf =3D 400, .nr =3D 9, .no =3D 2}, > + {.nf =3D 500, .nr =3D 9, .no =3D 2}, > + {.nf =3D 100, .nr =3D 3, .no =3D 1}, > + }; > + int cfg; > + > + switch (hz) { > + case 300000000: > + cfg =3D 0; > + break; > + case 533000000: /* actually 533.3P MHz */ > + cfg =3D 1; > + break; > + case 666000000: /* actually 666.6P MHz */ > + cfg =3D 2; > + break; > + case 800000000: > + cfg =3D 3; > + break; > + default: > + debug("%s: unsupported SDRAM frequency", __func__); > + return -EINVAL; > + } > + > + /* Enter PLL slow mode. */ > + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK, > + PLL_MODE_SLOW << DPLL_MODE_SHIFT); > + > + rk3066_clk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]); > + > + /* Wait for PLL lock. */ > + while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK)) > + udelay(1); > + > + /* Enter PLL normal mode. */ > + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK, > + PLL_MODE_NORMAL << DPLL_MODE_SHIFT); > + > + return 0; > +} > + > +static int rk3066_clk_configure_cpu(struct rk3066_cru *cru, struct rk3= 066_grf *grf, > + unsigned int hz) > +{ > + static const struct pll_div apll_cfg[] =3D { > + {.nf =3D 50, .nr =3D 1, .no =3D 2}, > + {.nf =3D 59, .nr =3D 1, .no =3D 1}, > + }; > + int div_core_peri, div_cpu_aclk, cfg; > + > + /* > + * We support two possible frequencies, the safe 600MHz > + * which will work with default pmic settings and will > + * be set to get away from the 24MHz default and > + * the maximum of 1.416Ghz, which boards can set if they > + * were able to get pmic support for it. > + */ > + switch (hz) { > + case APLL_SAFE_HZ: > + cfg =3D 0; > + div_core_peri =3D 1; > + div_cpu_aclk =3D 3; > + break; > + case APLL_HZ: > + cfg =3D 1; > + div_core_peri =3D 2; > + div_cpu_aclk =3D 3; > + break; > + default: > + debug("unsupported ARMCLK frequency"); > + return -EINVAL; > + } > + > + /* Enter PLL slow mode. */ > + rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK, > + PLL_MODE_SLOW << APLL_MODE_SHIFT); > + > + rk3066_clk_set_pll(cru, CLK_ARM, &apll_cfg[cfg]); > + > + /* Wait for PLL lock. */ > + while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK)) > + udelay(1); > + > + /* Set divider for peripherals attached to the CPU core. */ > + rk_clrsetreg(&cru->cru_clksel_con[0], > + CORE_PERI_DIV_MASK, > + div_core_peri << CORE_PERI_DIV_SHIFT); > + > + /* Set up dependent divisor for cpu_aclk. */ > + rk_clrsetreg(&cru->cru_clksel_con[1], > + CPU_ACLK_DIV_MASK, > + div_cpu_aclk << CPU_ACLK_DIV_SHIFT); > + > + /* Enter PLL normal mode. */ > + rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK, > + PLL_MODE_NORMAL << APLL_MODE_SHIFT); > + > + return hz; > +} > + > +static uint32_t rk3066_clk_pll_get_rate(struct rk3066_cru *cru, > + enum rk_clk_id clk_id) > +{ > + u32 nr, no, nf; > + u32 con; > + int pll_id =3D rk_pll_id(clk_id); > + struct rk3066_pll *pll =3D &cru->pll[pll_id]; > + static u8 clk_shift[CLK_COUNT] =3D { > + 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, > + GPLL_MODE_SHIFT > + }; > + uint shift; > + > + con =3D readl(&cru->cru_mode_con); > + shift =3D clk_shift[clk_id]; > + switch(FIELD_GET(APLL_MODE_MASK, con >> shift)) { > + case PLL_MODE_SLOW: > + return OSC_HZ; > + case PLL_MODE_NORMAL: > + /* normal mode */ > + con =3D readl(&pll->con0); > + no =3D bitfield_extract_by_mask(con, CLKOD_MASK) + 1; > + nr =3D bitfield_extract_by_mask(con, CLKR_MASK) + 1; > + con =3D readl(&pll->con1); > + nf =3D bitfield_extract_by_mask(con, CLKF_MASK) + 1; > + > + return (OSC_HZ * nf) / (nr * no); > + case PLL_MODE_DEEP: > + default: > + return 32768; > + } > +} > + > +static ulong rk3066_clk_mmc_get_clk(struct rk3066_cru *cru, uint gclk_= rate, > + int periph) > +{ > + uint div; > + u32 con; > + > + switch (periph) { > + case HCLK_EMMC: > + case SCLK_EMMC: > + con =3D readl(&cru->cru_clksel_con[12]); > + div =3D bitfield_extract_by_mask(con, EMMC_DIV_MASK); > + break; > + case HCLK_SDMMC: > + case SCLK_SDMMC: > + con =3D readl(&cru->cru_clksel_con[11]); > + div =3D bitfield_extract_by_mask(con, MMC0_DIV_MASK); > + break; > + case HCLK_SDIO: > + case SCLK_SDIO: > + con =3D readl(&cru->cru_clksel_con[12]); > + div =3D bitfield_extract_by_mask(con, SDIO_DIV_MASK); > + break; > + default: > + return -EINVAL; > + } > + > + return DIV_TO_RATE(gclk_rate, div) / 2; > +} > + > +static ulong rk3066_clk_mmc_set_clk(struct rk3066_cru *cru, uint gclk_= rate, > + int periph, uint freq) > +{ > + int src_clk_div; > + > + debug("%s: gclk_rate=3D%u\n", __func__, gclk_rate); > + /* MMC clock by default divides by 2 internally, so need to provide d= ouble in CRU. */ > + src_clk_div =3D DIV_ROUND_UP(gclk_rate / 2, freq) - 1; > + assert(src_clk_div <=3D 0x3f); > + > + switch (periph) { > + case HCLK_EMMC: > + case SCLK_EMMC: > + rk_clrsetreg(&cru->cru_clksel_con[12], > + EMMC_DIV_MASK, > + src_clk_div << EMMC_DIV_SHIFT); > + break; > + case HCLK_SDMMC: > + case SCLK_SDMMC: > + rk_clrsetreg(&cru->cru_clksel_con[11], > + MMC0_DIV_MASK, > + src_clk_div << MMC0_DIV_SHIFT); > + break; > + case HCLK_SDIO: > + case SCLK_SDIO: > + rk_clrsetreg(&cru->cru_clksel_con[12], > + SDIO_DIV_MASK, > + src_clk_div << SDIO_DIV_SHIFT); > + break; > + default: > + return -EINVAL; > + } > + > + return rk3066_clk_mmc_get_clk(cru, gclk_rate, periph); > +} > + > +static ulong rk3066_clk_spi_get_clk(struct rk3066_cru *cru, uint gclk_= rate, > + int periph) > +{ > + uint div; > + u32 con; > + > + switch (periph) { > + case SCLK_SPI0: > + con =3D readl(&cru->cru_clksel_con[25]); > + div =3D bitfield_extract_by_mask(con, SPI0_DIV_MASK); > + break; > + case SCLK_SPI1: > + con =3D readl(&cru->cru_clksel_con[25]); > + div =3D bitfield_extract_by_mask(con, SPI1_DIV_MASK); > + break; > + default: > + return -EINVAL; > + } > + > + return DIV_TO_RATE(gclk_rate, div); > +} > + > +static ulong rk3066_clk_spi_set_clk(struct rk3066_cru *cru, uint gclk_= rate, > + int periph, uint freq) > +{ > + int src_clk_div =3D DIV_ROUND_UP(gclk_rate, freq) - 1; > + > + assert(src_clk_div < 128); > + switch (periph) { > + case SCLK_SPI0: > + assert(src_clk_div <=3D SPI0_DIV_MASK >> SPI0_DIV_SHIFT); > + rk_clrsetreg(&cru->cru_clksel_con[25], > + SPI0_DIV_MASK, > + src_clk_div << SPI0_DIV_SHIFT); > + break; > + case SCLK_SPI1: > + assert(src_clk_div <=3D SPI1_DIV_MASK >> SPI1_DIV_SHIFT); > + rk_clrsetreg(&cru->cru_clksel_con[25], > + SPI1_DIV_MASK, > + src_clk_div << SPI1_DIV_SHIFT); > + break; > + default: > + return -EINVAL; > + } > + > + return rk3066_clk_spi_get_clk(cru, gclk_rate, periph); > +} > + > +static ulong rk3066_clk_saradc_get_clk(struct rk3066_cru *cru, int per= iph) > +{ > + u32 div, con; > + > + switch (periph) { > + case SCLK_SARADC: > + con =3D readl(&cru->cru_clksel_con[24]); > + div =3D bitfield_extract_by_mask(con, SARADC_DIV_MASK); > + break; > + case SCLK_TSADC: > + con =3D readl(&cru->cru_clksel_con[34]); > + div =3D bitfield_extract_by_mask(con, TSADC_DIV_MASK); > + break; > + default: > + return -EINVAL; > + } > + return DIV_TO_RATE(PERI_PCLK_HZ, div); > +} > + > +static ulong rk3066_clk_saradc_set_clk(struct rk3066_cru *cru, uint hz= , > + int periph) > +{ > + int src_clk_div; > + > + src_clk_div =3D DIV_ROUND_UP(PERI_PCLK_HZ, hz) - 1; > + assert(src_clk_div < 128); > + > + switch (periph) { > + case SCLK_SARADC: > + rk_clrsetreg(&cru->cru_clksel_con[24], > + SARADC_DIV_MASK, > + src_clk_div << SARADC_DIV_SHIFT); > + break; > + case SCLK_TSADC: > + rk_clrsetreg(&cru->cru_clksel_con[34], > + SARADC_DIV_MASK, > + src_clk_div << SARADC_DIV_SHIFT); > + break; > + default: > + return -EINVAL; > + } > + > + return rk3066_clk_saradc_get_clk(cru, periph); > +} > + > +static void rk3066_clk_init(struct rk3066_cru *cru, struct rk3066_grf = *grf) > +{ > + u32 aclk_div, hclk_div, pclk_div, h2p_div; > + > + /* Enter PLL slow mode. */ > + rk_clrsetreg(&cru->cru_mode_con, > + GPLL_MODE_MASK | > + CPLL_MODE_MASK, > + PLL_MODE_SLOW << GPLL_MODE_SHIFT | > + PLL_MODE_SLOW << CPLL_MODE_SHIFT); > + > + /* Init PLL. */ > + rk3066_clk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); > + rk3066_clk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); > + > + /* Wait for PLL lock. */ > + while ((readl(&grf->soc_status0) & > + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=3D > + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) > + udelay(1); > + > + /* > + * Select CPU clock PLL source and > + * reparent aclk_cpu_pre from APPL to GPLL. > + * Set up dependent divisors for PCLK/HCLK and ACLK clocks. > + */ > + aclk_div =3D DIV_ROUND_UP(GPLL_HZ, CPU_ACLK_HZ) - 1; > + assert((aclk_div + 1) * CPU_ACLK_HZ =3D=3D GPLL_HZ && aclk_div <=3D 0= x1f); > + > + rk_clrsetreg(&cru->cru_clksel_con[0], > + CPU_ACLK_PLL_MASK | > + A9_CORE_DIV_MASK, > + CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT | > + aclk_div << A9_CORE_DIV_SHIFT); > + > + hclk_div =3D ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ); > + assert((1 << hclk_div) * CPU_HCLK_HZ =3D=3D CPU_ACLK_HZ && hclk_div <= 0x3); > + pclk_div =3D ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ); > + assert((1 << pclk_div) * CPU_PCLK_HZ =3D=3D CPU_ACLK_HZ && pclk_div <= 0x4); > + h2p_div =3D ilog2(CPU_HCLK_HZ / CPU_H2P_HZ); > + assert((1 << h2p_div) * CPU_H2P_HZ =3D=3D CPU_HCLK_HZ && pclk_div < 0= x3); > + > + rk_clrsetreg(&cru->cru_clksel_con[1], > + AHB2APB_DIV_MASK | > + CPU_PCLK_DIV_MASK | > + CPU_HCLK_DIV_MASK, > + h2p_div << AHB2APB_DIV_SHIFT | > + pclk_div << CPU_PCLK_DIV_SHIFT | > + hclk_div << CPU_HCLK_DIV_SHIFT); > + > + /* > + * Select PERI clock PLL source and > + * set up dependent divisors for PCLK/HCLK and ACLK clocks. > + */ > + aclk_div =3D GPLL_HZ / PERI_ACLK_HZ - 1; > + assert((aclk_div + 1) * PERI_ACLK_HZ =3D=3D GPLL_HZ && aclk_div < 0x1= f); > + > + hclk_div =3D ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ); > + assert((1 << hclk_div) * PERI_HCLK_HZ =3D=3D > + PERI_ACLK_HZ && (hclk_div < 0x4)); > + > + pclk_div =3D ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ); > + assert((1 << pclk_div) * PERI_PCLK_HZ =3D=3D > + PERI_ACLK_HZ && (pclk_div < 0x4)); > + > + rk_clrsetreg(&cru->cru_clksel_con[10], > + PERI_PCLK_DIV_MASK | > + PERI_HCLK_DIV_MASK | > + PERI_ACLK_DIV_MASK, > + PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | > + pclk_div << PERI_PCLK_DIV_SHIFT | > + hclk_div << PERI_HCLK_DIV_SHIFT | > + aclk_div << PERI_ACLK_DIV_SHIFT); > + > + /* Enter PLL normal mode. */ > + rk_clrsetreg(&cru->cru_mode_con, > + GPLL_MODE_MASK | > + CPLL_MODE_MASK, > + PLL_MODE_NORMAL << GPLL_MODE_SHIFT | > + PLL_MODE_NORMAL << CPLL_MODE_SHIFT); > + > + rk3066_clk_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000); > +} > + > +static ulong rk3066_clk_get_rate(struct clk *clk) > +{ > + struct rk3066_clk_priv *priv =3D dev_get_priv(clk->dev); > + ulong new_rate, gclk_rate; > + > + gclk_rate =3D rk3066_clk_pll_get_rate(priv->cru, CLK_GENERAL); > + switch (clk->id) { > + case 1 ... 4: > + new_rate =3D rk3066_clk_pll_get_rate(priv->cru, clk->id); > + break; > + case HCLK_EMMC: > + case HCLK_SDMMC: > + case HCLK_SDIO: > + case SCLK_EMMC: > + case SCLK_SDMMC: > + case SCLK_SDIO: > + new_rate =3D rk3066_clk_mmc_get_clk(priv->cru, PERI_HCLK_HZ, > + clk->id); > + break; > + case SCLK_SPI0: > + case SCLK_SPI1: > + new_rate =3D rk3066_clk_spi_get_clk(priv->cru, PERI_PCLK_HZ, > + clk->id); > + break; > + case PCLK_I2C0: > + case PCLK_I2C1: > + case PCLK_I2C2: > + case PCLK_I2C3: > + case PCLK_I2C4: > + return gclk_rate; > + case SCLK_SARADC: > + case SCLK_TSADC: > + new_rate =3D rk3066_clk_saradc_get_clk(priv->cru, clk->id); > + break; > + case SCLK_TIMER0: > + case SCLK_TIMER1: > + case SCLK_TIMER2: > + case SCLK_UART0: > + case SCLK_UART1: > + case SCLK_UART2: > + case SCLK_UART3: > + return OSC_HZ; > + default: > + return -ENOENT; > + } > + > + return new_rate; > +} > + > +static ulong rk3066_clk_set_rate(struct clk *clk, ulong rate) > +{ > + struct rk3066_clk_priv *priv =3D dev_get_priv(clk->dev); > + struct rk3066_cru *cru =3D priv->cru; > + ulong new_rate; > + > + switch (clk->id) { > + case PLL_APLL: > + new_rate =3D rk3066_clk_configure_cpu(priv->cru, priv->grf, rate); > + break; > + case CLK_DDR: > + new_rate =3D rk3066_clk_configure_ddr(priv->cru, priv->grf, rate); > + break; > + case HCLK_EMMC: > + case HCLK_SDMMC: > + case HCLK_SDIO: > + case SCLK_EMMC: > + case SCLK_SDMMC: > + case SCLK_SDIO: > + new_rate =3D rk3066_clk_mmc_set_clk(cru, PERI_HCLK_HZ, > + clk->id, rate); > + break; > + case SCLK_SPI0: > + case SCLK_SPI1: > + new_rate =3D rk3066_clk_spi_set_clk(cru, PERI_PCLK_HZ, > + clk->id, rate); > + break; > + case SCLK_SARADC: > + case SCLK_TSADC: > + new_rate =3D rk3066_clk_saradc_set_clk(cru, rate, clk->id); > + break; > + case PLL_CPLL: > + case PLL_GPLL: > + case ACLK_CPU: > + case HCLK_CPU: > + case PCLK_CPU: > + case ACLK_PERI: > + case HCLK_PERI: > + case PCLK_PERI: > + return 0; > + default: > + return -ENOENT; > + } > + > + return new_rate; > +} > + > +static int rk3066_clk_enable(struct clk *clk) > +{ > + struct rk3066_clk_priv *priv =3D dev_get_priv(clk->dev); > + > + switch (clk->id) { > + case HCLK_NANDC0: > + rk_clrreg(&priv->cru->cru_clkgate_con[5], BIT(9)); > + break; > + case HCLK_SDMMC: > + rk_clrreg(&priv->cru->cru_clkgate_con[5], BIT(10)); > + break; > + case HCLK_SDIO: > + rk_clrreg(&priv->cru->cru_clkgate_con[5], BIT(11)); > + break; > + } > + > + return 0; > +} > + > +static int rk3066_clk_disable(struct clk *clk) > +{ > + struct rk3066_clk_priv *priv =3D dev_get_priv(clk->dev); > + > + switch (clk->id) { > + case HCLK_NANDC0: > + rk_setreg(&priv->cru->cru_clkgate_con[5], BIT(9)); > + break; > + case HCLK_SDMMC: > + rk_setreg(&priv->cru->cru_clkgate_con[5], BIT(10)); > + break; > + case HCLK_SDIO: > + rk_setreg(&priv->cru->cru_clkgate_con[5], BIT(11)); > + break; > + } > + > + return 0; > +} > + > +static struct clk_ops rk3066_clk_ops =3D { > + .disable =3D rk3066_clk_disable, > + .enable =3D rk3066_clk_enable, > + .get_rate =3D rk3066_clk_get_rate, > + .set_rate =3D rk3066_clk_set_rate, > +}; > + > +static int rk3066_clk_of_to_plat(struct udevice *dev) > +{ > + if (CONFIG_IS_ENABLED(OF_REAL)) { > + struct rk3066_clk_priv *priv =3D dev_get_priv(dev); > + > + priv->cru =3D dev_read_addr_ptr(dev); > + } > + > + return 0; > +} > + > +static int rk3066_clk_probe(struct udevice *dev) > +{ > + struct rk3066_clk_priv *priv =3D dev_get_priv(dev); > + > + priv->grf =3D syscon_get_first_range(ROCKCHIP_SYSCON_GRF); > + if (IS_ERR(priv->grf)) > + return PTR_ERR(priv->grf); > + > +#if CONFIG_IS_ENABLED(OF_PLATDATA) > + struct rk3066_clk_plat *plat =3D dev_get_plat(dev); > + > + priv->cru =3D map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); > +#endif > + > + if (IS_ENABLED(CONFIG_TPL_BUILD)) { > + rk3066_clk_init(priv->cru, priv->grf); > + > + /* Init CPU frequency. */ > + rk3066_clk_configure_cpu(priv->cru, priv->grf, APLL_SAFE_HZ); > + } > + > + return 0; > +} > + > +static int rk3066_clk_bind(struct udevice *dev) > +{ > + struct udevice *sys_child; > + struct sysreset_reg *priv; > + int reg_offset, ret; > + > + /* The reset driver does not have a device node, so bind it here. */ > + ret =3D device_bind(dev, DM_DRIVER_GET(sysreset_rockchip), "sysreset"= , > + NULL, ofnode_null(), &sys_child); > + if (ret) { > + dev_dbg(dev, "Warning: No sysreset driver: ret=3D%d\n", ret); > + } else { > + priv =3D malloc(sizeof(struct sysreset_reg)); > + priv->glb_srst_fst_value =3D offsetof(struct rk3066_cru, > + cru_glb_srst_fst_value); > + priv->glb_srst_snd_value =3D offsetof(struct rk3066_cru, > + cru_glb_srst_snd_value); > + dev_set_priv(sys_child, priv); > + } > + > + if (CONFIG_IS_ENABLED(RESET_ROCKCHIP)) { > + reg_offset =3D offsetof(struct rk3066_cru, cru_softrst_con[0]); > + ret =3D rockchip_reset_bind(dev, reg_offset, 9); > + if (ret) > + dev_dbg(dev, "Warning: software reset driver bind failed\n"); > + } > + > + return 0; > +} > + > +static const struct udevice_id rk3066_clk_ids[] =3D { > + { .compatible =3D "rockchip,rk3066a-cru" }, > + { } > +}; > + > +U_BOOT_DRIVER(rockchip_rk3066a_cru) =3D { > + .name =3D "rockchip_rk3066a_cru", > + .id =3D UCLASS_CLK, > + .ops =3D &rk3066_clk_ops, > + .probe =3D rk3066_clk_probe, > + .bind =3D rk3066_clk_bind, > + .of_match =3D rk3066_clk_ids, > + .of_to_plat =3D rk3066_clk_of_to_plat, > + .priv_auto =3D sizeof(struct rk3066_clk_priv), > + .plat_auto =3D sizeof(struct rk3066_clk_plat), > +}; >=20 Reviewed-by: Sean Anderson