From nobody Wed Dec 17 10:47:55 2025 Received: from sender4-pp-o94.zoho.com (sender4-pp-o94.zoho.com [136.143.188.94]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 987E7275105; Tue, 15 Apr 2025 14:26:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.94 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744727198; cv=pass; b=aNkN70STqn6+xIdk38eTNaEwF/ZIPs/zOxDUfV2SOUFnPNX0Og7HnQFibQa5VNmecaA6d2i8wTdNCVbea4+GEPOGGQYJFf6s2jG3hkdqRlYjhbwyX9kXaa56dDw3ZzPkSeR6HSUyk36cXSvwyMscIb095TVdp0Xf0ThSmax6fHg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744727198; c=relaxed/simple; bh=MBtbNO9Hf+dz7BWlE0u2kU2jdQpQfr485mBAiyn/haM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TAr+glwoCDyUc+1PaMxy3+qzkrtoJr90FAJpnF2ueir8maywv0yxpLRESEX8ThelTpzkaI+0ZhXCHNDhih1JtI3Ayw0WB9p/X9Rv90Dps0gQ4n7mgNusxlkJOZ54Vzof3koO1tWqQXGS5B95neoV5axxvnCafLitu8U9XpGiSLM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=zohomail.com; spf=pass smtp.mailfrom=zohomail.com; dkim=pass (1024-bit key) header.d=zohomail.com header.i=kingxukai@zohomail.com header.b=WOuTZAeZ; arc=pass smtp.client-ip=136.143.188.94 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=zohomail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zohomail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=zohomail.com header.i=kingxukai@zohomail.com header.b="WOuTZAeZ" ARC-Seal: i=1; a=rsa-sha256; t=1744727166; cv=none; d=zohomail.com; s=zohoarc; b=cAiaHhlKY2DY90ti3JtKrANy0JdifXwxXIxkDDt2mUErOtQEjy2Vxw1pt1RsMxVSvtMcFU2/asXWalNaq+XNiCfdOaDhuNI2lPJJowelvt0d0TalsjUmXd6m6OnysrIwfUYpfyJW5ofHFGXEd4LGwUMMw4/DwcxcWWZcC6tg134= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1744727166; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=MiKaFyLX75Sm9r6/FKWMvTQ+iD5uvLgM/00SDYJRLmc=; b=WOekWcMrOgatnkRYCViQSVVl+wwWo/KddlLwnRjY8sbQj4TBER+KQBIrgKP9IPVfquz6jx/wmtenea1AWt+W6gbRJLAMsoA/fKWOvBoMWaqv15gdOjL6thjz4Xu/iFOao92UFRXnBpIsDPlpPOBRNr3rj+h+hyOE+Z18SwShFM4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=zohomail.com; spf=pass smtp.mailfrom=kingxukai@zohomail.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1744727166; s=zm2022; d=zohomail.com; i=kingxukai@zohomail.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Feedback-ID:Reply-To; bh=MiKaFyLX75Sm9r6/FKWMvTQ+iD5uvLgM/00SDYJRLmc=; b=WOuTZAeZKwMrm32mymHcFbzuIKd0tlMTMt/jplcJ5NY/dYRHTQvu8U4q4xetfAlY J29yi7MhtnU+qb8qPSDqUFv3lnOAhZ2O7Lc79DdmhDP3MxcfU6VY/CfCTtLWUGcg9+p hAWu3WPCcIIq5oVOFGd/GFiFhBUdNIOU2B6F1DU8= Received: by mx.zohomail.com with SMTPS id 17447271627234.166329974105679; Tue, 15 Apr 2025 07:26:02 -0700 (PDT) From: Xukai Wang Date: Tue, 15 Apr 2025 22:25:12 +0800 Subject: [PATCH v6 2/3] clk: canaan: Add clock driver for Canaan K230 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250415-b4-k230-clk-v6-2-7fd89f427250@zohomail.com> References: <20250415-b4-k230-clk-v6-0-7fd89f427250@zohomail.com> In-Reply-To: <20250415-b4-k230-clk-v6-0-7fd89f427250@zohomail.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Xukai Wang , Paul Walmsley , Palmer Dabbelt , Albert Ou , Conor Dooley Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, Samuel Holland , Troy Mitchell X-Mailer: b4 0.14.2 Feedback-ID: rr080112271f81cff2612fbe5c89eb63b60000ca51b57bd58931e0524cf6c9ff9b55e4b2c83d9330dd1ad0c5:zu08011227e948a5eda6c2d61a2f49e15c000093186450c453c0613bc0459152e0d4b2a486cfcf7dda20abc9:rf0801122c3d564f7bdb8ed9fe27a7c8e000003b1fd56250917c440c54ae7a73b12e96e2cfdccf1ca0f3b1b8b6c541118f:ZohoMail X-ZohoMailClient: External This patch provides basic support for the K230 clock, which does not cover all clocks. The clock tree of the K230 SoC consists of OSC24M, PLLs and sysclk. Co-developed-by: Troy Mitchell Signed-off-by: Troy Mitchell Signed-off-by: Xukai Wang --- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-k230.c | 1710 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 1717 insertions(+) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 299bc678ed1b9fcd9110bb8c5937a1bd1ea60e23..1817b8883af9a3d00ac7af2cb88= 496274b591001 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -464,6 +464,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks. =20 +config COMMON_CLK_K230 + bool "Clock driver for the Canaan Kendryte K230 SoC" + depends on ARCH_CANAAN || COMPILE_TEST + help + Support for the Canaan Kendryte K230 RISC-V SoC clocks. + config COMMON_CLK_SP7021 tristate "Clock driver for Sunplus SP7021 SoC" depends on SOC_SP7021 || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fb8878a5d7d93da6bec487460cdf63f1f764a431..5df50b1e14c701ed38397bfb257= db26e8dd278b8 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_MACH_ASPEED_G6) +=3D clk-ast2600.o obj-$(CONFIG_ARCH_HIGHBANK) +=3D clk-highbank.o obj-$(CONFIG_CLK_HSDK) +=3D clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_K210) +=3D clk-k210.o +obj-$(CONFIG_COMMON_CLK_K230) +=3D clk-k230.o obj-$(CONFIG_LMK04832) +=3D clk-lmk04832.o obj-$(CONFIG_COMMON_CLK_LAN966X) +=3D clk-lan966x.o obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) +=3D clk-lochnagar.o diff --git a/drivers/clk/clk-k230.c b/drivers/clk/clk-k230.c new file mode 100644 index 0000000000000000000000000000000000000000..84a4a2a293e5f278d21510d7388= 8aee4ff9351df --- /dev/null +++ b/drivers/clk/clk-k230.c @@ -0,0 +1,1710 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Kendryte Canaan K230 Clock Drivers + * + * Author: Xukai Wang + * Author: Troy Mitchell + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* PLL control register bits. */ +#define K230_PLL_BYPASS_ENABLE BIT(19) +#define K230_PLL_GATE_ENABLE BIT(2) +#define K230_PLL_GATE_WRITE_ENABLE BIT(18) +#define K230_PLL_OD_SHIFT 24 +#define K230_PLL_OD_MASK 0xF +#define K230_PLL_R_SHIFT 16 +#define K230_PLL_R_MASK 0x3F +#define K230_PLL_F_SHIFT 0 +#define K230_PLL_F_MASK 0x1FFFF +#define K230_PLL0_OFFSET_BASE 0x00 +#define K230_PLL1_OFFSET_BASE 0x10 +#define K230_PLL2_OFFSET_BASE 0x20 +#define K230_PLL3_OFFSET_BASE 0x30 +#define K230_PLL_DIV_REG_OFFSET 0x00 +#define K230_PLL_BYPASS_REG_OFFSET 0x04 +#define K230_PLL_GATE_REG_OFFSET 0x08 +#define K230_PLL_LOCK_REG_OFFSET 0x0C + +/* PLL lock register bits. */ +#define K230_PLL_STATUS_MASK BIT(0) + +/* K230 CLK registers offset */ +#define K230_CLK_AUDIO_CLKDIV_OFFSET 0x34 +#define K230_CLK_PDM_CLKDIV_OFFSET 0x40 +#define K230_CLK_CODEC_ADC_MCLKDIV_OFFSET 0x38 +#define K230_CLK_CODEC_DAC_MCLKDIV_OFFSET 0x3c + +/* K230 CLK OPS. */ +#define K230_CLK_OPS_GATE \ + .enable =3D k230_clk_enable, \ + .disable =3D k230_clk_disable, \ + .is_enabled =3D k230_clk_is_enabled + +#define K230_CLK_OPS_RATE \ + .set_rate =3D k230_clk_set_rate, \ + .round_rate =3D k230_clk_round_rate, \ + .recalc_rate =3D k230_clk_get_rate + +#define K230_CLK_OPS_MUX \ + .set_parent =3D k230_clk_set_parent, \ + .get_parent =3D k230_clk_get_parent, \ + .determine_rate =3D clk_hw_determine_rate_no_reparent + +#define K230_CLK_OPS_ID_NONE 0 +#define K230_CLK_OPS_ID_GATE_ONLY 1 +#define K230_CLK_OPS_ID_RATE_ONLY 2 +#define K230_CLK_OPS_ID_RATE_GATE 3 +#define K230_CLK_OPS_ID_MUX_ONLY 4 +#define K230_CLK_OPS_ID_MUX_GATE 5 +#define K230_CLK_OPS_ID_MUX_RATE 6 +#define K230_CLK_OPS_ID_ALL 7 +#define K230_CLK_OPS_ID_NUM 8 + +/* K230 CLK MACROS */ +#define K230_CLK_MAX_PARENT_NUM 6 + +#define K230_GATE_FORMAT(_reg, _bit, _reverse) \ + .gate_reg_off =3D (_reg), \ + .gate_bit_enable =3D (_bit), \ + .gate_bit_reverse =3D (_reverse) + +#define K230_RATE_FORMAT(_mul_min, _mul_max, _mul_shift, _mul_mask, \ + _div_min, _div_max, _div_shift, _div_mask, \ + _reg, _bit, _method) \ + .rate_mul_min =3D (_mul_min), \ + .rate_mul_max =3D (_mul_max), \ + .rate_mul_shift =3D (_mul_shift), \ + .rate_mul_mask =3D (_mul_mask), \ + .rate_div_min =3D (_div_min), \ + .rate_div_max =3D (_div_max), \ + .rate_div_shift =3D (_div_shift), \ + .rate_div_mask =3D (_div_mask), \ + .rate_reg_off =3D (_reg), \ + .rate_write_enable_bit =3D (_bit), \ + .method =3D (_method) + +#define K230_RATE_C_FORMAT(_mul_min, _mul_max, _mul_shift, _mul_mask, \ + _reg, _bit) \ + .rate_mul_min_c =3D (_mul_min), \ + .rate_mul_max_c =3D (_mul_max), \ + .rate_mul_shift_c =3D (_mul_shift), \ + .rate_mul_mask_c =3D (_mul_mask), \ + .rate_reg_off_c =3D (_reg), \ + .rate_write_enable_bit_c =3D (_bit) + +#define K230_MUX_FORMAT(_reg, _shift, _mask) \ + .mux_reg_off =3D (_reg), \ + .mux_reg_shift =3D (_shift), \ + .mux_reg_mask =3D (_mask) + +struct k230_sysclk; + +enum k230_pll_id { + K230_PLL0, + K230_PLL1, + K230_PLL2, + K230_PLL3, + K230_PLL_NUM +}; + +struct k230_pll { + enum k230_pll_id id; + struct k230_sysclk *ksc; + void __iomem *div, *bypass, *gate, *lock; + struct clk_hw hw; +}; + +#define to_k230_pll(_hw) container_of(_hw, struct k230_pll, hw) + +struct k230_pll_cfg { + u32 reg; + const char *name; + struct k230_pll *pll; +}; + +struct k230_pll_div { + struct k230_sysclk *ksc; + struct clk_hw *hw; +}; + +struct k230_pll_div_cfg { + const char *parent_name, *name; + int div; + struct k230_pll_div *pll_div; +}; + +enum k230_pll_div_id { + K230_PLL0_DIV2, + K230_PLL0_DIV3, + K230_PLL0_DIV4, + K230_PLL0_DIV16, + K230_PLL1_DIV2, + K230_PLL1_DIV3, + K230_PLL1_DIV4, + K230_PLL2_DIV2, + K230_PLL2_DIV3, + K230_PLL2_DIV4, + K230_PLL3_DIV2, + K230_PLL3_DIV3, + K230_PLL3_DIV4, + K230_PLL_DIV_NUM +}; + +enum k230_clk_div_type { + K230_MUL, + K230_DIV, + K230_MUL_DIV, +}; + +struct k230_clk { + int id; + struct k230_sysclk *ksc; + struct clk_hw hw; +}; + +#define to_k230_clk(_hw) container_of(_hw, struct k230_clk, hw) + +struct k230_sysclk { + struct platform_device *pdev; + void __iomem *pll_regs, *regs; + spinlock_t pll_lock, clk_lock; + struct k230_pll *plls; + struct k230_clk *clks; + struct k230_pll_div *dclks; +}; + +struct k230_clk_rate_cfg { + /* rate reg */ + u32 rate_reg_off; + void __iomem *rate_reg; + /* rate info*/ + u32 rate_write_enable_bit; + enum k230_clk_div_type method; + /* rate mul */ + u32 rate_mul_min; + u32 rate_mul_max; + u32 rate_mul_shift; + u32 rate_mul_mask; + /* rate div */ + u32 rate_div_min; + u32 rate_div_max; + u32 rate_div_shift; + u32 rate_div_mask; +}; + +struct k230_clk_rate_cfg_c { + /* rate_c reg */ + u32 rate_reg_off_c; + void __iomem *rate_reg_c; + + /* rate_c info */ + u32 rate_write_enable_bit_c; + + /* rate mul-changable */ + u32 rate_mul_min_c; + u32 rate_mul_max_c; + u32 rate_mul_shift_c; + u32 rate_mul_mask_c; +}; + +struct k230_clk_gate_cfg { + /* gate reg */ + u32 gate_reg_off; + void __iomem *gate_reg; + + /* gate info*/ + u32 gate_bit_enable; + bool gate_bit_reverse; +}; + +struct k230_clk_mux_cfg { + /* mux reg */ + u32 mux_reg_off; + void __iomem *mux_reg; + + /* mux info */ + u32 mux_reg_shift; + u32 mux_reg_mask; +}; + +enum k230_clk_parent_type { + K230_OSC24M, + K230_PLL, + K230_PLL_DIV, + K230_CLK_COMPOSITE, +}; + +struct k230_clk_cfg; + +struct k230_clk_parent { + enum k230_clk_parent_type type; + union { + struct k230_pll_cfg *pll_cfg; + struct k230_pll_div_cfg *pll_div_cfg; + struct k230_clk_cfg *clk_cfg; + }; +}; + +struct k230_clk_cfg { + /* attr */ + const char *name; + + /* 0-read & write; 1-read only */ + bool read_only; + int num_parent; + struct k230_clk_parent parent[K230_CLK_MAX_PARENT_NUM]; + struct k230_clk *clk; + int flags; + + /* cfgs */ + struct k230_clk_rate_cfg *rate_cfg; + struct k230_clk_rate_cfg_c *rate_cfg_c; + struct k230_clk_gate_cfg *gate_cfg; + struct k230_clk_mux_cfg *mux_cfg; +}; + +static struct k230_pll_cfg k230_pll_cfgs[] =3D { + [K230_PLL0] =3D { + .reg =3D K230_PLL0_OFFSET_BASE, + .name =3D "pll0", + .pll =3D NULL, + }, + [K230_PLL1] =3D { + .reg =3D K230_PLL1_OFFSET_BASE, + .name =3D "pll1", + .pll =3D NULL, + }, + [K230_PLL2] =3D { + .reg =3D K230_PLL2_OFFSET_BASE, + .name =3D "pll2", + .pll =3D NULL, + }, + [K230_PLL3] =3D { + .reg =3D K230_PLL3_OFFSET_BASE, + .name =3D "pll3", + .pll =3D NULL, + }, +}; + +static struct k230_pll_div_cfg k230_pll_div_cfgs[] =3D { + [K230_PLL0_DIV2] =3D { "pll0", "pll0_div2", 2, NULL}, + [K230_PLL0_DIV3] =3D { "pll0", "pll0_div3", 3, NULL}, + [K230_PLL0_DIV4] =3D { "pll0", "pll0_div4", 4, NULL}, + [K230_PLL0_DIV16] =3D { "pll0", "pll0_div16", 16, NULL}, + [K230_PLL1_DIV2] =3D { "pll1", "pll1_div2", 2, NULL}, + [K230_PLL1_DIV3] =3D { "pll1", "pll1_div3", 3, NULL}, + [K230_PLL1_DIV4] =3D { "pll1", "pll1_div4", 4, NULL}, + [K230_PLL2_DIV2] =3D { "pll2", "pll2_div2", 2, NULL}, + [K230_PLL2_DIV3] =3D { "pll2", "pll2_div3", 3, NULL}, + [K230_PLL2_DIV4] =3D { "pll2", "pll2_div4", 4, NULL}, + [K230_PLL3_DIV2] =3D { "pll3", "pll3_div2", 2, NULL}, + [K230_PLL3_DIV3] =3D { "pll3", "pll3_div3", 3, NULL}, + [K230_PLL3_DIV4] =3D { "pll3", "pll3_div4", 4, NULL}, +}; + +static struct k230_clk_rate_cfg k230_cpu0_src_rate =3D { + K230_RATE_FORMAT(1, 16, 0, 0, + 16, 16, 1, 0xf, + 0x0, 31, K230_MUL) +}; + +static struct k230_clk_gate_cfg k230_cpu0_src_gate =3D { + K230_GATE_FORMAT(0, 0, false) +}; + +static struct k230_clk_rate_cfg k230_cpu0_aclk_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x0, 31, K230_DIV) +}; + +static struct k230_clk_rate_cfg k230_cpu0_plic_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 10, 0x7, + 0x0, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_cpu0_plic_gate =3D { + K230_GATE_FORMAT(0x0, 9, false) +}; + +static struct k230_clk_gate_cfg k230_cpu0_noc_ddrcp4_gate =3D { + K230_GATE_FORMAT(0x60, 7, false) +}; + +static struct k230_clk_rate_cfg k230_cpu0_pclk_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x0, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_cpu0_pclk_gate =3D { + K230_GATE_FORMAT(0x0, 13, false) +}; + +static struct k230_clk_gate_cfg k230_pmu_pclk_gate =3D { + K230_GATE_FORMAT(0x10, 0, false) +}; + +static struct k230_clk_gate_cfg k230_hs_ospi_src_gate =3D { + K230_GATE_FORMAT(0x18, 24, false) +}; + +static struct k230_clk_mux_cfg k230_hs_ospi_src_mux =3D { + K230_MUX_FORMAT(0x20, 18, 0x1) +}; + +static struct k230_clk_rate_cfg k230_ls_apb_src_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x30, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_apb_src_gate =3D { + K230_GATE_FORMAT(0x24, 0, false) +}; + +static struct k230_clk_gate_cfg k230_ls_uart0_apb_gate =3D { + K230_GATE_FORMAT(0x24, 1, false) +}; + +static struct k230_clk_gate_cfg k230_ls_uart1_apb_gate =3D { + K230_GATE_FORMAT(0x24, 2, false) +}; + +static struct k230_clk_gate_cfg k230_ls_uart2_apb_gate =3D { + K230_GATE_FORMAT(0x24, 3, false) +}; + +static struct k230_clk_gate_cfg k230_ls_uart3_apb_gate =3D { + K230_GATE_FORMAT(0x24, 4, false) +}; + +static struct k230_clk_gate_cfg k230_ls_uart4_apb_gate =3D { + K230_GATE_FORMAT(0x24, 5, false) +}; + +static struct k230_clk_rate_cfg k230_ls_uart0_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x2C, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_uart0_gate =3D { + K230_GATE_FORMAT(0x24, 16, false) +}; + +static struct k230_clk_rate_cfg k230_ls_uart1_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x2C, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_uart1_gate =3D { + K230_GATE_FORMAT(0x24, 17, false) +}; + +static struct k230_clk_rate_cfg k230_ls_uart2_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x2C, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_uart2_gate =3D { + K230_GATE_FORMAT(0x24, 18, false) +}; + +static struct k230_clk_rate_cfg k230_ls_uart3_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x2C, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_uart3_gate =3D { + K230_GATE_FORMAT(0x24, 19, false) +}; + +static struct k230_clk_rate_cfg k230_ls_uart4_rate =3D { + K230_RATE_FORMAT(1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x2C, 31, K230_DIV) +}; + +static struct k230_clk_gate_cfg k230_ls_uart4_gate =3D { + K230_GATE_FORMAT(0x24, 20, false) +}; + +static struct k230_clk_gate_cfg k230_shrm_axi_src_gate =3D { + K230_GATE_FORMAT(0x5C, 12, false) +}; + +static struct k230_clk_gate_cfg k230_shrm_sdma_axi_gate =3D { + K230_GATE_FORMAT(0x5C, 5, false) +}; + +static struct k230_clk_gate_cfg k230_shrm_pdma_axi_gate =3D { + K230_GATE_FORMAT(0x5C, 3, false) +}; + +static struct k230_clk_cfg k230_cpu0_src =3D { + .name =3D "cpu0_src", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV2], + }, + .rate_cfg =3D &k230_cpu0_src_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_cpu0_src_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_cpu0_aclk =3D { + .name =3D "cpu0_aclk", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_cpu0_src, + }, + .rate_cfg =3D &k230_cpu0_aclk_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D NULL, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_cpu0_plic =3D { + .name =3D "cpu0_plic", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_cpu0_src, + + }, + .rate_cfg =3D &k230_cpu0_plic_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_cpu0_plic_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_cpu0_noc_ddrcp4 =3D { + .name =3D "cpu0_noc_ddrcp4", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_cpu0_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_cpu0_noc_ddrcp4_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_cpu0_pclk =3D { + .name =3D "cpu0_pclk", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV4], + }, + .rate_cfg =3D &k230_cpu0_pclk_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_cpu0_pclk_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_pmu_pclk =3D { + .name =3D "pmu_pclk", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_OSC24M, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_pmu_pclk_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_hs_ospi_src =3D { + .name =3D "hs_ospi_src", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 2, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV2], + }, + .parent[1] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL2_DIV4], + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_hs_ospi_src_gate, + .mux_cfg =3D &k230_hs_ospi_src_mux, +}; + +static struct k230_clk_cfg k230_ls_apb_src =3D { + .name =3D "ls_apb_src", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV4], + }, + .rate_cfg =3D &k230_ls_apb_src_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_apb_src_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart0_apb =3D { + .name =3D "ls_uart0_apb", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_ls_apb_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart0_apb_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart1_apb =3D { + .name =3D "ls_uart1_apb", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_ls_apb_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart1_apb_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart2_apb =3D { + .name =3D "ls_uart2_apb", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_ls_apb_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart2_apb_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart3_apb =3D { + .name =3D "ls_uart3_apb", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_ls_apb_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart3_apb_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart4_apb =3D { + .name =3D "ls_uart4_apb", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_ls_apb_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart4_apb_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart0 =3D { + .name =3D "ls_uart0", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV16], + }, + .rate_cfg =3D &k230_ls_uart0_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart0_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart1 =3D { + .name =3D "ls_uart1", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV16], + }, + .rate_cfg =3D &k230_ls_uart1_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart1_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart2 =3D { + .name =3D "ls_uart2", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV16], + }, + .rate_cfg =3D &k230_ls_uart2_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart2_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart3 =3D { + .name =3D "ls_uart3", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV16], + }, + .rate_cfg =3D &k230_ls_uart3_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart3_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_ls_uart4 =3D { + .name =3D "ls_uart4", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV16], + }, + .rate_cfg =3D &k230_ls_uart4_rate, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_ls_uart4_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_shrm_axi_src =3D { + .name =3D "shrm_axi_src", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_PLL_DIV, + .pll_div_cfg =3D &k230_pll_div_cfgs[K230_PLL0_DIV4], + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_shrm_axi_src_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_shrm_sdma_axi =3D { + .name =3D "shrm_sdma_axi", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_shrm_axi_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_shrm_sdma_axi_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg k230_shrm_pdma_axi =3D { + .name =3D "shrm_pdma_axi", + .read_only =3D false, + .flags =3D 0, + .num_parent =3D 1, + .parent[0] =3D { + .type =3D K230_CLK_COMPOSITE, + .clk_cfg =3D &k230_shrm_axi_src, + }, + .rate_cfg =3D NULL, + .rate_cfg_c =3D NULL, + .gate_cfg =3D &k230_shrm_pdma_axi_gate, + .mux_cfg =3D NULL, +}; + +static struct k230_clk_cfg *k230_clk_cfgs[] =3D { + [K230_CPU0_SRC] =3D &k230_cpu0_src, + [K230_CPU0_ACLK] =3D &k230_cpu0_aclk, + [K230_CPU0_PLIC] =3D &k230_cpu0_plic, + [K230_CPU0_NOC_DDRCP4] =3D &k230_cpu0_noc_ddrcp4, + [K230_CPU0_PCLK] =3D &k230_cpu0_pclk, + [K230_PMU_PCLK] =3D &k230_pmu_pclk, + [K230_HS_OSPI_SRC] =3D &k230_hs_ospi_src, + [K230_LS_APB_SRC] =3D &k230_ls_apb_src, + [K230_LS_UART0_APB] =3D &k230_ls_uart0_apb, + [K230_LS_UART1_APB] =3D &k230_ls_uart1_apb, + [K230_LS_UART2_APB] =3D &k230_ls_uart2_apb, + [K230_LS_UART3_APB] =3D &k230_ls_uart3_apb, + [K230_LS_UART4_APB] =3D &k230_ls_uart4_apb, + [K230_LS_UART0] =3D &k230_ls_uart0, + [K230_LS_UART1] =3D &k230_ls_uart1, + [K230_LS_UART2] =3D &k230_ls_uart2, + [K230_LS_UART3] =3D &k230_ls_uart3, + [K230_LS_UART4] =3D &k230_ls_uart4, + [K230_SHRM_AXI_SRC] =3D &k230_shrm_axi_src, + [K230_SHRM_SDMA_AXI_GATE] =3D &k230_shrm_sdma_axi, + [K230_SHRM_PDMA_AXI_GATE] =3D &k230_shrm_pdma_axi, +}; + +#define K230_CLK_NUM ARRAY_SIZE(k230_clk_cfgs) + +static void k230_init_pll(void __iomem *regs, enum k230_pll_id pll_id, + struct k230_pll *pll) +{ + void __iomem *base; + + pll->id =3D pll_id; + base =3D regs + k230_pll_cfgs[pll_id].reg; + pll->div =3D base + K230_PLL_DIV_REG_OFFSET; + pll->bypass =3D base + K230_PLL_BYPASS_REG_OFFSET; + pll->gate =3D base + K230_PLL_GATE_REG_OFFSET; + pll->lock =3D base + K230_PLL_LOCK_REG_OFFSET; +} + +static int k230_pll_prepare(struct clk_hw *hw) +{ + struct k230_pll *pll =3D to_k230_pll(hw); + u32 reg; + + /* wait for PLL lock until it reaches lock status */ + return readl_poll_timeout(pll->lock, reg, + (reg & K230_PLL_STATUS_MASK) =3D=3D K230_PLL_STATUS_MASK, + 400, 0); +} + +static bool k230_pll_hw_is_enabled(struct k230_pll *pll) +{ + return (readl(pll->gate) & K230_PLL_GATE_ENABLE) =3D=3D K230_PLL_GATE_ENA= BLE; +} + +static void k230_pll_enable_hw(void __iomem *regs, struct k230_pll *pll) +{ + u32 reg; + + if (k230_pll_hw_is_enabled(pll)) + return; + + /* Set PLL factors */ + reg =3D readl(pll->gate); + reg |=3D (K230_PLL_GATE_ENABLE | K230_PLL_GATE_WRITE_ENABLE); + writel(reg, pll->gate); +} + +static int k230_pll_enable(struct clk_hw *hw) +{ + struct k230_pll *pll =3D to_k230_pll(hw); + struct k230_sysclk *ksc =3D pll->ksc; + + guard(spinlock)(&ksc->pll_lock); + k230_pll_enable_hw(ksc->regs, pll); + + return 0; +} + +static void k230_pll_disable(struct clk_hw *hw) +{ + struct k230_pll *pll =3D to_k230_pll(hw); + struct k230_sysclk *ksc =3D pll->ksc; + u32 reg; + + guard(spinlock)(&ksc->pll_lock); + reg =3D readl(pll->gate); + reg &=3D ~(K230_PLL_GATE_ENABLE); + reg |=3D (K230_PLL_GATE_WRITE_ENABLE); + writel(reg, pll->gate); +} + +static int k230_pll_is_enabled(struct clk_hw *hw) +{ + return k230_pll_hw_is_enabled(to_k230_pll(hw)); +} + +static int k230_pll_init(struct clk_hw *hw) +{ + if (k230_pll_is_enabled(hw)) + return clk_prepare_enable(hw->clk); + + return 0; +} + +static unsigned long k230_pll_get_rate(struct clk_hw *hw, unsigned long pa= rent_rate) +{ + struct k230_pll *pll =3D to_k230_pll(hw); + struct k230_sysclk *ksc =3D pll->ksc; + u32 reg; + u32 r, f, od; + + reg =3D readl(pll->bypass); + if (reg & K230_PLL_BYPASS_ENABLE) + return parent_rate; + + reg =3D readl(pll->lock); + if (!(reg & (K230_PLL_STATUS_MASK))) { + dev_err(&ksc->pdev->dev, "%s is unlock.\n", clk_hw_get_name(hw)); + return 0; + } + + reg =3D readl(pll->div); + r =3D ((reg >> K230_PLL_R_SHIFT) & K230_PLL_R_MASK) + 1; + f =3D ((reg >> K230_PLL_F_SHIFT) & K230_PLL_F_MASK) + 1; + od =3D ((reg >> K230_PLL_OD_SHIFT) & K230_PLL_OD_MASK) + 1; + + return mul_u64_u32_div(parent_rate, f, r * od); +} + +static const struct clk_ops k230_pll_ops =3D { + .init =3D k230_pll_init, + .prepare =3D k230_pll_prepare, + .enable =3D k230_pll_enable, + .disable =3D k230_pll_disable, + .is_enabled =3D k230_pll_is_enabled, + .recalc_rate =3D k230_pll_get_rate, +}; + +static int k230_register_pll(struct platform_device *pdev, + struct k230_sysclk *ksc, + enum k230_pll_id pll_id, + const char *name, + int num_parents, + const struct clk_ops *ops) +{ + struct k230_pll *pll =3D &ksc->plls[pll_id]; + struct clk_init_data init =3D {}; + struct device *dev =3D &pdev->dev; + int ret; + const struct clk_parent_data parent_data[] =3D { + { .index =3D 0, }, + }; + + init.name =3D name; + init.parent_data =3D parent_data; + init.num_parents =3D num_parents; + init.ops =3D ops; + + pll->hw.init =3D &init; + pll->ksc =3D ksc; + + ret =3D devm_clk_hw_register(dev, &pll->hw); + if (ret) + return ret; + + k230_pll_cfgs[pll_id].pll =3D pll; + + return 0; +} + +static int k230_register_plls(struct platform_device *pdev, struct k230_sy= sclk *ksc) +{ + int i, ret; + const struct k230_pll_cfg *cfg; + + for (i =3D 0; i < K230_PLL_NUM; i++) { + cfg =3D &k230_pll_cfgs[i]; + + k230_init_pll(ksc->pll_regs, i, &ksc->plls[i]); + + ret =3D k230_register_pll(pdev, ksc, i, cfg->name, 1, &k230_pll_ops); + if (ret) + return ret; + } + + return 0; +} + +static int k230_register_pll_divs(struct platform_device *pdev, struct k23= 0_sysclk *ksc) +{ + struct device *dev =3D &pdev->dev; + struct k230_pll_div *pll_div; + struct clk_hw *hw; + + for (int i =3D 0; i < K230_PLL_DIV_NUM; i++) { + hw =3D devm_clk_hw_register_fixed_factor(dev, k230_pll_div_cfgs[i].name, + k230_pll_div_cfgs[i].parent_name, + 0, 1, k230_pll_div_cfgs[i].div); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + pll_div =3D &ksc->dclks[i]; + pll_div->hw =3D hw; + pll_div->ksc =3D ksc; + k230_pll_div_cfgs[i].pll_div =3D pll_div; + } + + return 0; +} + +static int k230_clk_enable(struct clk_hw *hw) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_gate_cfg *gate_cfg =3D cfg->gate_cfg; + u32 reg; + + guard(spinlock)(&ksc->clk_lock); + reg =3D readl(gate_cfg->gate_reg); + if (gate_cfg->gate_bit_reverse) + reg &=3D ~BIT(gate_cfg->gate_bit_enable); + else + reg |=3D BIT(gate_cfg->gate_bit_enable); + writel(reg, gate_cfg->gate_reg); + + return 0; +} + +static void k230_clk_disable(struct clk_hw *hw) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_gate_cfg *gate_cfg =3D cfg->gate_cfg; + u32 reg; + + guard(spinlock)(&ksc->clk_lock); + reg =3D readl(gate_cfg->gate_reg); + + if (gate_cfg->gate_bit_reverse) + reg |=3D BIT(gate_cfg->gate_bit_enable); + else + reg &=3D ~BIT(gate_cfg->gate_bit_enable); + + writel(reg, gate_cfg->gate_reg); +} + +static int k230_clk_is_enabled(struct clk_hw *hw) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_gate_cfg *gate_cfg =3D cfg->gate_cfg; + u32 reg; + + guard(spinlock)(&ksc->clk_lock); + reg =3D readl(gate_cfg->gate_reg); + + /* Check gate bit condition based on configuration and then set ret */ + if (gate_cfg->gate_bit_reverse) + return (BIT(gate_cfg->gate_bit_enable) & reg) ? 1 : 0; + else + return (BIT(gate_cfg->gate_bit_enable) & ~reg) ? 1 : 0; +} + +static int k230_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_mux_cfg *mux_cfg =3D cfg->mux_cfg; + u8 reg; + + guard(spinlock)(&ksc->clk_lock); + reg =3D (mux_cfg->mux_reg_mask & index) << mux_cfg->mux_reg_shift; + writeb(reg, mux_cfg->mux_reg); + + return 0; +} + +static u8 k230_clk_get_parent(struct clk_hw *hw) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_mux_cfg *mux_cfg =3D cfg->mux_cfg; + u8 reg; + + guard(spinlock)(&ksc->clk_lock); + reg =3D readb(mux_cfg->mux_reg); + + return reg; +} + +static unsigned long k230_clk_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_rate_cfg *rate_cfg =3D cfg->rate_cfg; + struct k230_clk_rate_cfg_c *rate_cfg_c =3D cfg->rate_cfg_c; + u32 mul, div; + + if (!rate_cfg) /* no divider, return parents' clk */ + return parent_rate; + + guard(spinlock)(&ksc->clk_lock); + switch (rate_cfg->method) { + /* + * K230_MUL: div_mask+1/div_max... + * K230_DIV: mul_max/div_mask+1 + * K230_MUL_DIV: mul_mask/div_mask... + */ + case K230_MUL: + div =3D rate_cfg->rate_div_max; + mul =3D (readl(rate_cfg->rate_reg) >> rate_cfg->rate_div_shift) + & rate_cfg->rate_div_mask; + mul++; + break; + case K230_DIV: + mul =3D rate_cfg->rate_mul_max; + div =3D (readl(rate_cfg->rate_reg) >> rate_cfg->rate_div_shift) + & rate_cfg->rate_div_mask; + div++; + break; + case K230_MUL_DIV: + if (!rate_cfg_c) { + mul =3D (readl(rate_cfg->rate_reg) >> rate_cfg->rate_mul_shift) + & rate_cfg->rate_mul_mask; + div =3D (readl(rate_cfg->rate_reg) >> rate_cfg->rate_div_shift) + & rate_cfg->rate_div_mask; + } else { + mul =3D (readl(rate_cfg_c->rate_reg_c) >> rate_cfg_c->rate_mul_shift_c) + & rate_cfg_c->rate_mul_mask_c; + div =3D (readl(rate_cfg->rate_reg) >> rate_cfg->rate_div_shift) + & rate_cfg->rate_div_mask; + } + break; + } + + return mul_u64_u32_div(parent_rate, mul, div); +} + +static int k230_clk_find_approximate(struct k230_clk *clk, + u32 mul_min, + u32 mul_max, + u32 div_min, + u32 div_max, + enum k230_clk_div_type method, + unsigned long rate, + unsigned long parent_rate, + u32 *div, + u32 *mul) +{ + long abs_min; + long abs_current; + long perfect_divide; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_rate_cfg *rate_cfg =3D cfg->rate_cfg; + + const u32 codec_clk[9] =3D { + 2048000, + 3072000, + 4096000, + 6144000, + 8192000, + 11289600, + 12288000, + 24576000, + 49152000 + }; + + const u32 codec_div[9][2] =3D { + {3125, 16}, + {3125, 24}, + {3125, 32}, + {3125, 48}, + {3125, 64}, + {15625, 441}, + {3125, 96}, + {3125, 192}, + {3125, 384} + }; + + const u32 pdm_clk[20] =3D { + 128000, + 192000, + 256000, + 384000, + 512000, + 768000, + 1024000, + 1411200, + 1536000, + 2048000, + 2822400, + 3072000, + 4096000, + 5644800, + 6144000, + 8192000, + 11289600, + 12288000, + 24576000, + 49152000 + }; + + const u32 pdm_div[20][2] =3D { + {3125, 1}, + {6250, 3}, + {3125, 2}, + {3125, 3}, + {3125, 4}, + {3125, 6}, + {3125, 8}, + {125000, 441}, + {3125, 12}, + {3125, 16}, + {62500, 441}, + {3125, 24}, + {3125, 32}, + {31250, 441}, + {3125, 48}, + {3125, 64}, + {15625, 441}, + {3125, 96}, + {3125, 192}, + {3125, 384} + }; + + switch (method) { + /* only mul can be changeable 1/12,2/12,3/12...*/ + case K230_MUL: + perfect_divide =3D (long)((parent_rate * 1000) / rate); + abs_min =3D abs(perfect_divide - + (long)(((long)div_max * 1000) / (long)mul_min)); + *mul =3D mul_min; + + for (u32 i =3D mul_min + 1; i <=3D mul_max; i++) { + abs_current =3D abs(perfect_divide - + (long)((long)((long)div_max * 1000) / (long)i)); + if (abs_min > abs_current) { + abs_min =3D abs_current; + *mul =3D i; + } + } + + *div =3D div_max; + break; + /* only div can be changeable, 1/1,1/2,1/3...*/ + case K230_DIV: + perfect_divide =3D (long)((parent_rate * 1000) / rate); + abs_min =3D abs(perfect_divide - + (long)(((long)div_min * 1000) / (long)mul_max)); + *div =3D div_min; + + for (u32 i =3D div_min + 1; i <=3D div_max; i++) { + abs_current =3D abs(perfect_divide - + (long)((long)((long)i * 1000) / (long)mul_max)); + if (abs_min > abs_current) { + abs_min =3D abs_current; + *div =3D i; + } + } + + *mul =3D mul_max; + break; + /* mul and div can be changeable. */ + case K230_MUL_DIV: + if (rate_cfg->rate_reg_off =3D=3D K230_CLK_CODEC_ADC_MCLKDIV_OFFSET || + rate_cfg->rate_reg_off =3D=3D K230_CLK_CODEC_DAC_MCLKDIV_OFFSET) { + for (u32 j =3D 0; j < 9; j++) { + if (0 =3D=3D (rate - codec_clk[j])) { + *div =3D codec_div[j][0]; + *mul =3D codec_div[j][1]; + } + } + } else if (rate_cfg->rate_reg_off =3D=3D K230_CLK_AUDIO_CLKDIV_OFFSET || + rate_cfg->rate_reg_off =3D=3D K230_CLK_PDM_CLKDIV_OFFSET) { + for (u32 j =3D 0; j < 20; j++) { + if (0 =3D=3D (rate - pdm_clk[j])) { + *div =3D pdm_div[j][0]; + *mul =3D pdm_div[j][1]; + } + } + } else { + return -EINVAL; + } + break; + } + + return 0; +} + +static long k230_clk_round_rate(struct clk_hw *hw, unsigned long rate, uns= igned long *parent_rate) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_rate_cfg *rate_cfg =3D cfg->rate_cfg; + u32 div =3D 0, mul =3D 0; + + if (k230_clk_find_approximate(clk, + rate_cfg->rate_mul_min, rate_cfg->rate_mul_max, + rate_cfg->rate_div_min, rate_cfg->rate_div_max, + rate_cfg->method, rate, *parent_rate, &div, &mul)) { + return 0; + } + + return mul_u64_u32_div(*parent_rate, mul, div); +} + +static int k230_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct k230_clk *clk =3D to_k230_clk(hw); + struct k230_sysclk *ksc =3D clk->ksc; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[clk->id]; + struct k230_clk_rate_cfg *rate_cfg =3D cfg->rate_cfg; + struct k230_clk_rate_cfg_c *rate_cfg_c =3D cfg->rate_cfg_c; + u32 div =3D 0, mul =3D 0, reg =3D 0, reg_c; + + if (rate > parent_rate || rate =3D=3D 0 || parent_rate =3D=3D 0) { + dev_err(&ksc->pdev->dev, "rate or parent_rate error\n"); + return -EINVAL; + } + + if (cfg->read_only) { + dev_err(&ksc->pdev->dev, "This clk rate is read only\n"); + return -EPERM; + } + + if (k230_clk_find_approximate(clk, + rate_cfg->rate_mul_min, rate_cfg->rate_mul_max, + rate_cfg->rate_div_min, rate_cfg->rate_div_max, + rate_cfg->method, rate, parent_rate, &div, &mul)) { + return -EINVAL; + } + + guard(spinlock)(&ksc->clk_lock); + if (!rate_cfg_c) { + reg =3D readl(rate_cfg->rate_reg); + reg &=3D ~((rate_cfg->rate_div_mask) << (rate_cfg->rate_div_shift)); + + if (rate_cfg->method =3D=3D K230_DIV) { + reg &=3D ~((rate_cfg->rate_mul_mask) << (rate_cfg->rate_mul_shift)); + reg |=3D ((div - 1) & rate_cfg->rate_div_mask) << (rate_cfg->rate_div_s= hift); + } else if (rate_cfg->method =3D=3D K230_MUL) { + reg |=3D ((mul - 1) & rate_cfg->rate_div_mask) << (rate_cfg->rate_div_s= hift); + } else { + reg |=3D (mul & rate_cfg->rate_mul_mask) << (rate_cfg->rate_mul_shift); + reg |=3D (div & rate_cfg->rate_div_mask) << (rate_cfg->rate_div_shift); + } + reg |=3D BIT(rate_cfg->rate_write_enable_bit); + } else { + reg =3D readl(rate_cfg->rate_reg); + reg_c =3D readl(rate_cfg_c->rate_reg_c); + reg &=3D ~((rate_cfg->rate_div_mask) << (rate_cfg->rate_div_shift)); + reg_c &=3D ~((rate_cfg_c->rate_mul_mask_c) << (rate_cfg_c->rate_mul_shif= t_c)); + reg_c |=3D BIT(rate_cfg_c->rate_write_enable_bit_c); + + reg_c |=3D (mul & rate_cfg_c->rate_mul_mask_c) << (rate_cfg_c->rate_mul_= shift_c); + reg |=3D (div & rate_cfg->rate_div_mask) << (rate_cfg->rate_div_shift); + + writel(reg_c, rate_cfg_c->rate_reg_c); + } + writel(reg, rate_cfg->rate_reg); + + return 0; +} + +static const struct clk_ops k230_clk_ops_arr[K230_CLK_OPS_ID_NUM] =3D { + [K230_CLK_OPS_ID_NONE] =3D { + /* Sentinel */ + }, + [K230_CLK_OPS_ID_GATE_ONLY] =3D { + K230_CLK_OPS_GATE, + }, + [K230_CLK_OPS_ID_RATE_ONLY] =3D { + K230_CLK_OPS_RATE, + }, + [K230_CLK_OPS_ID_RATE_GATE] =3D { + K230_CLK_OPS_RATE, + K230_CLK_OPS_GATE, + }, + [K230_CLK_OPS_ID_MUX_ONLY] =3D { + K230_CLK_OPS_MUX, + }, + [K230_CLK_OPS_ID_MUX_GATE] =3D { + K230_CLK_OPS_MUX, + K230_CLK_OPS_GATE, + }, + [K230_CLK_OPS_ID_MUX_RATE] =3D { + K230_CLK_OPS_MUX, + K230_CLK_OPS_RATE, + }, + [K230_CLK_OPS_ID_ALL] =3D { + K230_CLK_OPS_MUX, + K230_CLK_OPS_RATE, + K230_CLK_OPS_GATE, + }, +}; + +static int k230_register_clk(struct platform_device *pdev, + struct k230_sysclk *ksc, + int id, + const struct clk_parent_data *parent_data, + u8 num_parents, + unsigned long flags) +{ + struct k230_clk *clk =3D &ksc->clks[id]; + struct k230_clk_cfg *cfg =3D k230_clk_cfgs[id]; + struct k230_clk_gate_cfg *gate_cfg =3D cfg->gate_cfg; + struct k230_clk_rate_cfg *rate_cfg =3D cfg->rate_cfg; + struct k230_clk_mux_cfg *mux_cfg =3D cfg->mux_cfg; + struct k230_clk_rate_cfg_c *rate_cfg_c =3D cfg->rate_cfg_c; + struct clk_init_data init =3D {}; + int clk_id =3D 0; + int ret; + + if (rate_cfg) { + rate_cfg->rate_reg =3D ksc->regs + rate_cfg->rate_reg_off; + clk_id +=3D K230_CLK_OPS_ID_RATE_ONLY; + } + + if (mux_cfg) { + mux_cfg->mux_reg =3D ksc->regs + mux_cfg->mux_reg_off; + clk_id +=3D K230_CLK_OPS_ID_MUX_ONLY; + + /* mux clock doesn't match the case that num_parents less than 2 */ + if (num_parents < 2) + return -EINVAL; + } + + if (gate_cfg) { + gate_cfg->gate_reg =3D ksc->regs + gate_cfg->gate_reg_off; + clk_id +=3D K230_CLK_OPS_ID_GATE_ONLY; + } + + if (rate_cfg_c) + rate_cfg_c->rate_reg_c =3D ksc->regs + rate_cfg_c->rate_reg_off_c; + + init.name =3D k230_clk_cfgs[id]->name; + init.flags =3D flags; + init.parent_data =3D parent_data; + init.num_parents =3D num_parents; + init.ops =3D &k230_clk_ops_arr[clk_id]; + + clk->id =3D id; + clk->ksc =3D ksc; + clk->hw.init =3D &init; + + ret =3D devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + + k230_clk_cfgs[id]->clk =3D clk; + + return 0; +} + +static int k230_register_mux_clk(struct platform_device *pdev, + struct k230_sysclk *ksc, + struct clk_parent_data *parent_data, + int num_parent, + int id) +{ + return k230_register_clk(pdev, ksc, id, parent_data, num_parent, 0); +} + +static int k230_register_osc24m_child(struct platform_device *pdev, + struct k230_sysclk *ksc, + int id) +{ + const struct clk_parent_data parent_data =3D { + .index =3D 0, + }; + return k230_register_clk(pdev, ksc, id, &parent_data, 1, 0); +} + +static int k230_register_pll_child(struct platform_device *pdev, + struct k230_sysclk *ksc, + int id, + struct clk_hw *parent_hw, + unsigned long flags) +{ + const struct clk_parent_data parent_data =3D { + .hw =3D parent_hw, + }; + return k230_register_clk(pdev, ksc, id, &parent_data, 1, flags); +} + +static int k230_register_pll_div_child(struct platform_device *pdev, + struct k230_sysclk *ksc, + int id, + struct clk_hw *parent_hw, + unsigned long flags) +{ + const struct clk_parent_data parent_data =3D { + .hw =3D parent_hw, + }; + return k230_register_clk(pdev, ksc, id, &parent_data, 1, flags); +} + +static int k230_register_clk_child(struct platform_device *pdev, + struct k230_sysclk *ksc, + int id, + struct clk_hw *parent_hw) +{ + const struct clk_parent_data parent_data =3D { + .hw =3D parent_hw, + }; + return k230_register_clk(pdev, ksc, id, &parent_data, 1, 0); +} + +static int k230_clk_get_parent_data(struct k230_clk_parent *pclk, + struct clk_parent_data *parent_data) +{ + switch (pclk->type) { + case K230_OSC24M: + parent_data->index =3D 0; + break; + case K230_PLL: + parent_data->hw =3D &pclk->pll_cfg->pll->hw; + break; + case K230_PLL_DIV: + parent_data->hw =3D pclk->pll_div_cfg->pll_div->hw; + break; + case K230_CLK_COMPOSITE: + parent_data->hw =3D &pclk->clk_cfg->clk->hw; + break; + } + + return 0; +} + +static int k230_clk_mux_get_parent_data(struct k230_clk_cfg *cfg, + struct clk_parent_data *parent_data) +{ + int ret; + struct k230_clk_parent *pclk =3D cfg->parent; + + for (int i =3D 0; i < cfg->num_parent; i++) { + ret =3D k230_clk_get_parent_data(&pclk[i], &parent_data[i]); + if (ret) + return ret; + } + + return 0; +} + +static int k230_register_clks(struct platform_device *pdev, struct k230_sy= sclk *ksc) +{ + struct k230_clk_cfg *cfg; + struct k230_clk_parent *pclk; + struct clk_parent_data parent_data[K230_CLK_MAX_PARENT_NUM]; + int ret, i; + + /* + * Single parent clock: + * pll0_div2 sons: cpu0_src + * pll0_div4 sons: cpu0_pclk + * cpu0_src sons: cpu0_aclk, cpu0_plic, cpu0_noc_ddrcp4, pmu_pclk + * + * Mux clock: + * hs_ospi_src parents: pll0_div2, pll2_div4 + */ + for (i =3D 0; i < K230_CLK_NUM; i++) { + cfg =3D k230_clk_cfgs[i]; + if (!cfg) + continue; + + if (cfg->mux_cfg) { + ret =3D k230_clk_mux_get_parent_data(cfg, parent_data); + if (ret) + return ret; + + ret =3D k230_register_mux_clk(pdev, ksc, parent_data, + cfg->num_parent, i); + } else { + pclk =3D cfg->parent; + switch (pclk->type) { + case K230_OSC24M: + ret =3D k230_register_osc24m_child(pdev, ksc, i); + break; + case K230_PLL: + ret =3D k230_register_pll_child(pdev, ksc, i, + &pclk->pll_cfg->pll->hw, + cfg->flags); + break; + case K230_PLL_DIV: + ret =3D k230_register_pll_div_child(pdev, ksc, i, + pclk->pll_div_cfg->pll_div->hw, + cfg->flags); + break; + case K230_CLK_COMPOSITE: + ret =3D k230_register_clk_child(pdev, ksc, i, + &pclk->clk_cfg->clk->hw); + break; + } + } + if (ret) + return ret; + } + + return 0; +} + +static struct clk_hw *k230_clk_hw_onecell_get(struct of_phandle_args *clks= pec, void *data) +{ + struct k230_sysclk *ksc; + unsigned int idx; + + if (clkspec->args_count !=3D 1) + return ERR_PTR(-EINVAL); + + idx =3D clkspec->args[0]; + if (idx >=3D K230_CLK_NUM) + return ERR_PTR(-EINVAL); + + if (!data) + return ERR_PTR(-EINVAL); + + ksc =3D data; + + return &ksc->clks[idx].hw; +} + +static int k230_clk_init_plls(struct platform_device *pdev) +{ + int ret; + struct k230_sysclk *ksc =3D platform_get_drvdata(pdev); + + spin_lock_init(&ksc->pll_lock); + + ksc->pll_regs =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ksc->pll_regs)) + return dev_err_probe(&pdev->dev, PTR_ERR(ksc->pll_regs), "map registers = failed\n"); + + ret =3D k230_register_plls(pdev, ksc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "register plls failed\n"); + + ret =3D k230_register_pll_divs(pdev, ksc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "register pll_divs failed\n"); + + for (int i =3D 0; i < K230_PLL_DIV_NUM; i++) { + ret =3D devm_clk_hw_register_clkdev(&pdev->dev, ksc->dclks[i].hw, + k230_pll_div_cfgs[i].name, NULL); + if (ret) + return dev_err_probe(&pdev->dev, ret, "clock_lookup create failed\n"); + } + + return 0; +} + +static int k230_clk_init_clks(struct platform_device *pdev) +{ + int ret; + struct k230_sysclk *ksc =3D platform_get_drvdata(pdev); + + spin_lock_init(&ksc->clk_lock); + + ksc->regs =3D devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(ksc->regs)) + return dev_err_probe(&pdev->dev, PTR_ERR(ksc->regs), "failed to map regi= sters\n"); + + ret =3D k230_register_clks(pdev, ksc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "register clock provider failed\n"= ); + + ret =3D devm_of_clk_add_hw_provider(&pdev->dev, k230_clk_hw_onecell_get, = ksc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "add clock provider failed\n"); + + return 0; +} + +static int k230_clk_probe(struct platform_device *pdev) +{ + int ret; + struct k230_sysclk *ksc; + + ksc =3D devm_kzalloc(&pdev->dev, sizeof(*ksc), GFP_KERNEL); + if (!ksc) + return -ENOMEM; + + ksc->plls =3D devm_kcalloc(&pdev->dev, K230_PLL_NUM, + sizeof(*ksc->plls), GFP_KERNEL); + if (!ksc->plls) + return -ENOMEM; + + ksc->dclks =3D devm_kcalloc(&pdev->dev, K230_PLL_DIV_NUM, + sizeof(*ksc->dclks), GFP_KERNEL); + if (!ksc->dclks) + return -ENOMEM; + + ksc->clks =3D devm_kcalloc(&pdev->dev, K230_CLK_NUM, + sizeof(*ksc->clks), GFP_KERNEL); + if (!ksc->clks) + return -ENOMEM; + + ksc->pdev =3D pdev; + platform_set_drvdata(pdev, ksc); + + ret =3D k230_clk_init_plls(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init plls failed\n"); + + ret =3D k230_clk_init_clks(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init clks failed\n"); + + return 0; +} + +static const struct of_device_id k230_clk_ids[] =3D { + { .compatible =3D "canaan,k230-clk" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, k230_clk_ids); + +static struct platform_driver k230_clk_driver =3D { + .driver =3D { + .name =3D "k230_clock_controller", + .of_match_table =3D k230_clk_ids, + }, + .probe =3D k230_clk_probe, +}; +builtin_platform_driver(k230_clk_driver); --=20 2.34.1