[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4062F0ACD5F73215+aKQBl3LMPkTacr9O@LT-Guozexi>
Date: Tue, 19 Aug 2025 12:46:15 +0800
From: Troy Mitchell <troy.mitchell@...ux.spacemit.com>
To: dongxuyang@...incomputing.com, mturquette@...libre.com,
sboyd@...nel.org, robh@...nel.org, krzk+dt@...nel.org,
conor+dt@...nel.org, linux-clk@...r.kernel.org,
devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
paul.walmsley@...ive.com, palmer@...belt.com, aou@...s.berkeley.edu,
alex@...ti.fr, linux-riscv@...ts.infradead.org
Cc: ningyu@...incomputing.com, linmin@...incomputing.com,
huangyifeng@...incomputing.com, pinkesh.vaghela@...fochips.com,
Troy Mitchell <troy.mitchell@...ux.spacemit.com>
Subject: Re: [PATCH v4 2/3] clock: eswin: Add eic7700 clock driver
On Fri, Aug 15, 2025 at 05:37:20PM +0800, dongxuyang@...incomputing.com wrote:
> From: Xuyang Dong <dongxuyang@...incomputing.com>
>
> This driver depends on the CCF framework implementation.
> Based on this driver, other modules in the SoC can use the APIs
> provided by CCF to perform clock-related operations.
> The driver supports eic7700 series chips.
Please align and modify according to Krzysztof's suggestion.
>
> Signed-off-by: Yifeng Huang <huangyifeng@...incomputing.com>
> Signed-off-by: Xuyang Dong <dongxuyang@...incomputing.com>
> ---
> drivers/clk/Kconfig | 1 +
> drivers/clk/Makefile | 1 +
> drivers/clk/eswin/Kconfig | 10 +
> drivers/clk/eswin/Makefile | 8 +
> drivers/clk/eswin/clk-eic7700.c | 44 ++
> drivers/clk/eswin/clk.c | 734 ++++++++++++++++++++++++++++++++
> drivers/clk/eswin/clk.h | 69 +++
> 7 files changed, 867 insertions(+)
> create mode 100644 drivers/clk/eswin/Kconfig
> create mode 100644 drivers/clk/eswin/Makefile
> create mode 100644 drivers/clk/eswin/clk-eic7700.c
> create mode 100644 drivers/clk/eswin/clk.c
> create mode 100644 drivers/clk/eswin/clk.h
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 4d56475f94fc..184b76a406d7 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -505,6 +505,7 @@ source "drivers/clk/actions/Kconfig"
> source "drivers/clk/analogbits/Kconfig"
> source "drivers/clk/baikal-t1/Kconfig"
> source "drivers/clk/bcm/Kconfig"
> +source "drivers/clk/eswin/Kconfig"
> source "drivers/clk/hisilicon/Kconfig"
> source "drivers/clk/imgtec/Kconfig"
> source "drivers/clk/imx/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 18ed29cfdc11..42c61e216511 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -120,6 +120,7 @@ obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/
> obj-y += bcm/
> obj-$(CONFIG_ARCH_BERLIN) += berlin/
> obj-$(CONFIG_ARCH_DAVINCI) += davinci/
> +obj-$(CONFIG_ARCH_ESWIN) += eswin/
> obj-$(CONFIG_ARCH_HISI) += hisilicon/
> obj-y += imgtec/
> obj-y += imx/
> diff --git a/drivers/clk/eswin/Kconfig b/drivers/clk/eswin/Kconfig
> new file mode 100644
> index 000000000000..f2284c2d790d
> --- /dev/null
> +++ b/drivers/clk/eswin/Kconfig
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config COMMON_CLK_EIC7700
> + bool "EIC7700 Clock Driver"
> + depends on ARCH_ESWIN
also COMPILE_TEST here.
> + help
> + Build the Eswin EIC7700 SoC clock driver based on the
> + common clock framework. This driver provides support
> + for the clock control on the Eswin EIC7700 SoC,
no need to mention CCF.
> + which is essential for managing clock rates and power management.
> diff --git a/drivers/clk/eswin/Makefile b/drivers/clk/eswin/Makefile
> new file mode 100644
> index 000000000000..a3139e34ee22
> --- /dev/null
> +++ b/drivers/clk/eswin/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Eswin Clock specific Makefile
> +#
> +
> +obj-y += clk.o
Should we always compile this?
- Troy
> +
> +obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o
> diff --git a/drivers/clk/eswin/clk-eic7700.c b/drivers/clk/eswin/clk-eic7700.c
> new file mode 100644
> index 000000000000..278b256b4c52
> --- /dev/null
> +++ b/drivers/clk/eswin/clk-eic7700.c
> @@ -0,0 +1,44 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd..
> + * All rights reserved.
> + *
> + * ESWIN EIC7700 CLK Provider Driver
> + *
> + * Authors:
> + * Yifeng Huang <huangyifeng@...incomputing.com>
> + * Xuyang Dong <dongxuyang@...incomputing.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include "clk.h"
> +
> +static void __init eic7700_clk_pll_init(struct device_node *np)
> +{
> + eswin_clk_pll_register(np);
> +}
> +
> +static void __init eic7700_clk_mux_init(struct device_node *np)
> +{
> + eswin_clk_mux_register(np);
> +}
> +
> +static void __init eic7700_clk_div_init(struct device_node *np)
> +{
> + eswin_clk_div_register(np);
> +}
> +
> +static void __init eic7700_clk_gate_init(struct device_node *np)
> +{
> + eswin_clk_gate_register(np);
> +}
> +
> +CLK_OF_DECLARE(eic7700_clk_pll, "eswin,pll-clock", eic7700_clk_pll_init);
> +CLK_OF_DECLARE(eic7700_clk_mux, "eswin,mux-clock", eic7700_clk_mux_init);
> +CLK_OF_DECLARE(eic7700_clk_div, "eswin,divider-clock", eic7700_clk_div_init);
> +CLK_OF_DECLARE(eic7700_clk_gate, "eswin,gate-clock", eic7700_clk_gate_init);
> diff --git a/drivers/clk/eswin/clk.c b/drivers/clk/eswin/clk.c
> new file mode 100644
> index 000000000000..e227cc4664ca
> --- /dev/null
> +++ b/drivers/clk/eswin/clk.c
> @@ -0,0 +1,734 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd..
> + * All rights reserved.
> + *
> + * Authors:
> + * Yifeng Huang <huangyifeng@...incomputing.com>
> + * Xuyang Dong <dongxuyang@...incomputing.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/math.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +#include <linux/util_macros.h>
> +#include "clk.h"
> +
> +enum pll_clk {
> + CLK_APLL_FOUT1 = 1,
> + CLK_PLL_CPU
> +};
> +
> +static enum pll_clk str_to_pll_clk(const char *str)
> +{
> + if (!strcmp(str, "clk_apll_fout1"))
> + return CLK_APLL_FOUT1;
> + else if (!strcmp(str, "clk_pll_cpu"))
> + return CLK_PLL_CPU;
> + else
> + return 0;
> +}
> +
> +static void __iomem *parent_base;
> +
> +static void __init get_parent_base(struct device_node *parent_np)
> +{
> + if (!parent_base) {
> + parent_base = of_iomap(parent_np, 0);
> + if (IS_ERR(parent_base)) {
> + pr_err("%s: Failed to map registers\n", __func__);
> + parent_base = NULL;
> + }
> + }
> +}
> +
> +/**
> + * eswin_calc_pll - calculate PLL values
> + * @frac_val: fractional divider
> + * @fbdiv_val: feedback divider
> + * @rate: reference rate
> + *
> + * Calculate PLL values for frac and fbdiv
> + */
> +static void eswin_calc_pll(u32 *frac_val, u32 *fbdiv_val, u64 rate)
> +{
> + u32 rem, tmp1, tmp2;
> +
> + rate = rate * 4;
> + rem = do_div(rate, 1000);
> + if (rem)
> + tmp1 = rem;
> +
> + rem = do_div(rate, 1000);
> + if (rem)
> + tmp2 = rem;
> +
> + rem = do_div(rate, 24);
> + /* fbdiv = rate * 4 / 24000000 */
> + *fbdiv_val = rate;
> + /* frac = rate * 4 % 24000000 * (2 ^ 24) */
> + *frac_val = ((1000 * (1000 * rem + tmp2) + tmp1) << 21) / 3 / 1000000;
> +}
> +
> +static inline struct eswin_clk_pll *to_pll_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct eswin_clk_pll, hw);
> +}
> +
> +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct eswin_clk_pll *clk = to_pll_clk(hw);
> + const char *clk_name = clk_hw_get_name(&clk->hw);
> +
> + if (!clk_name)
> + return -ENOMEM;
> +
> + u32 frac_val = 0, fbdiv_val, refdiv_val = 1, postdiv1_val = 0;
> + u32 val;
> + int ret;
> + struct clk *clk_cpu_mux = NULL;
> + struct clk *clk_cpu_lp_pll = NULL;
> + struct clk *clk_cpu_pll = NULL;
> + int try_count = 0;
> + bool lock_flag = false;
> +
> + eswin_calc_pll(&frac_val, &fbdiv_val, (u64)rate);
> +
> + /* Must switch the CPU to other CLK before we change the CPU PLL. */
> + if (str_to_pll_clk(clk_name) == CLK_PLL_CPU) {
> + clk_cpu_mux = __clk_lookup("mux_cpu_root_3mux1");
> + if (!clk_cpu_mux) {
> + pr_err("%s %d, failed to get %s\n", __func__, __LINE__,
> + "mux_cpu_root_3mux1");
> + return -EINVAL;
> + }
> + clk_cpu_lp_pll = __clk_lookup("fixed_factor_u84_core_lp_div2");
> + if (!clk_cpu_lp_pll) {
> + pr_err("%s %d, failed to get %s\n", __func__, __LINE__,
> + "fixed_factor_u84_core_lp_div2");
> + return -EINVAL;
> + }
> + ret = clk_prepare_enable(clk_cpu_lp_pll);
> + if (ret) {
> + pr_err("%s %d, failed to enable %s, ret %d\n",
> + __func__, __LINE__,
> + "fixed_factor_u84_core_lp_div2", ret);
> + return ret;
> + }
> + clk_cpu_pll = __clk_lookup("clk_pll_cpu");
> + if (!clk_cpu_pll) {
> + pr_err("%s %d, failed to get %s\n", __func__, __LINE__,
> + "clk_pll_cpu");
> + clk_disable_unprepare(clk_cpu_lp_pll);
> + return -EINVAL;
> + }
> +
> + ret = clk_set_parent(clk_cpu_mux, clk_cpu_lp_pll);
> + if (ret) {
> + pr_err("%s %d, failed to switch %s to %s, ret %d\n",
> + __func__, __LINE__, "mux_cpu_root_3mux1",
> + "fixed_factor_u84_core_lp_div2", ret);
> + clk_disable_unprepare(clk_cpu_lp_pll);
> + return -EPERM;
> + }
> + }
> +
> + /* first disable PLL */
> + val = readl_relaxed(clk->ctrl_reg0);
> + val &= ~(((1 << clk->pllen_width) - 1) << clk->pllen_shift);
> + val |= 0 << clk->pllen_shift;
> + writel_relaxed(val, clk->ctrl_reg0);
> +
> + val = readl_relaxed(clk->ctrl_reg0);
> + val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift);
> + val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift);
> + val |= refdiv_val << clk->refdiv_shift;
> + val |= fbdiv_val << clk->fbdiv_shift;
> + writel_relaxed(val, clk->ctrl_reg0);
> +
> + val = readl_relaxed(clk->ctrl_reg1);
> + val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift);
> + val |= frac_val << clk->frac_shift;
> + writel_relaxed(val, clk->ctrl_reg1);
> +
> + val = readl_relaxed(clk->ctrl_reg2);
> + val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift);
> + val |= postdiv1_val << clk->postdiv1_shift;
> + writel_relaxed(val, clk->ctrl_reg2);
> +
> + /* at last, enable PLL */
> + val = readl_relaxed(clk->ctrl_reg0);
> + val &= ~(((1 << clk->pllen_width) - 1) << clk->pllen_shift);
> + val |= 1 << clk->pllen_shift;
> + writel_relaxed(val, clk->ctrl_reg0);
> +
> + /* usually the PLL would lock in 50us */
> + do {
> + usleep_range(refdiv_val * 80, refdiv_val * 80 * 2);
> + val = readl_relaxed(clk->status_reg);
> + if (val & 1 << clk->lock_shift) {
> + lock_flag = true;
> + break;
> + }
> + } while (try_count++ < 10);
> +
> + if (!lock_flag) {
> + pr_err("%s %d, failed to lock the cpu pll", __func__, __LINE__);
> + return -EBUSY;
> + }
> +
> + if (str_to_pll_clk(clk_name) == CLK_PLL_CPU) {
> + ret = clk_set_parent(clk_cpu_mux, clk_cpu_pll);
> + if (ret) {
> + pr_err("%s %d, failed to switch %s to %s, ret %d\n",
> + __func__, __LINE__, "mux_cpu_root_3mux1",
> + "clk_pll_cpu", ret);
> + return -EPERM;
> + }
> + clk_disable_unprepare(clk_cpu_lp_pll);
> + }
> + return ret;
> +}
> +
> +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct eswin_clk_pll *clk = to_pll_clk(hw);
> + const char *clk_name = clk_hw_get_name(&clk->hw);
> +
> + if (!clk_name)
> + return -ENOMEM;
> +
> + u64 frac_val, fbdiv_val, refdiv_val, tmp, rem;
> + u32 postdiv1_val;
> + u32 val;
> + u64 rate = 0;
> +
> + val = readl_relaxed(clk->ctrl_reg0);
> + val = val >> clk->fbdiv_shift;
> + val &= ((1 << clk->fbdiv_width) - 1);
> + fbdiv_val = val;
> +
> + val = readl_relaxed(clk->ctrl_reg0);
> + val = val >> clk->refdiv_shift;
> + val &= ((1 << clk->refdiv_width) - 1);
> + refdiv_val = val;
> +
> + val = readl_relaxed(clk->ctrl_reg1);
> + val = val >> clk->frac_shift;
> + val &= ((1 << clk->frac_width) - 1);
> + frac_val = val;
> +
> + val = readl_relaxed(clk->ctrl_reg2);
> + val = val >> clk->postdiv1_shift;
> + val &= ((1 << clk->postdiv1_width) - 1);
> + postdiv1_val = val;
> +
> + /* rate = 24000000 * (fbdiv + frac / (2 ^ 24)) / 4 */
> + if (str_to_pll_clk(clk_name)) {
> + tmp = 1000 * frac_val;
> + rem = do_div(tmp, BIT(24));
> + if (rem)
> + rate = (u64)(6000 * (1000 * fbdiv_val + tmp) +
> + ((6000 * rem) >> 24) + 1);
> + else
> + rate = (u64)(6000 * 1000 * fbdiv_val);
> + }
> +
> + return rate;
> +}
> +
> +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *parent_rate)
> +{
> + struct eswin_clk_pll *clk = to_pll_clk(hw);
> + const char *clk_name = clk_hw_get_name(&clk->hw);
> +
> + if (!clk_name)
> + return -ENOMEM;
> +
> + int index;
> + u64 round_rate = 0;
> +
> + /* Must be sorted in ascending order */
> + u64 apll_clk[] = { APLL_LOW_FREQ, APLL_HIGH_FREQ };
> + u64 cpu_pll_clk[] = { CLK_FREQ_100M, CLK_FREQ_200M, CLK_FREQ_400M,
> + CLK_FREQ_500M, CLK_FREQ_600M, CLK_FREQ_700M,
> + CLK_FREQ_800M, CLK_FREQ_900M, CLK_FREQ_1000M,
> + CLK_FREQ_1200M, CLK_FREQ_1300M, CLK_FREQ_1400M,
> + CLK_FREQ_1500M, CLK_FREQ_1600M, CLK_FREQ_1700M,
> + CLK_FREQ_1800M };
> +
> + switch (str_to_pll_clk(clk_name)) {
> + case CLK_APLL_FOUT1:
> + index = find_closest(rate, apll_clk, ARRAY_SIZE(apll_clk));
> + round_rate = apll_clk[index];
> + break;
> + case CLK_PLL_CPU:
> + index = find_closest(rate, cpu_pll_clk,
> + ARRAY_SIZE(cpu_pll_clk));
> + round_rate = cpu_pll_clk[index];
> + break;
> + default:
> + pr_err("%s %d, unknown clk %s\n", __func__, __LINE__,
> + clk_name);
> + break;
> + }
> + return round_rate;
> +}
> +
> +static const struct clk_ops eswin_clk_pll_ops = {
> + .set_rate = clk_pll_set_rate,
> + .recalc_rate = clk_pll_recalc_rate,
> + .round_rate = clk_pll_round_rate,
> +};
> +
> +void __init eswin_clk_gate_register(struct device_node *np)
> +{
> + struct clk_hw *clk_hw;
> + struct device_node *parent_np;
> + const char *clk_name;
> + const char *parent_name;
> + u32 idx_bit;
> + u32 reg;
> + int ret;
> +
> + parent_np = of_get_parent(np);
> + if (!parent_np) {
> + pr_err("%s: Failed to get parent node\n", __func__);
> + return;
> + }
> +
> + if (of_device_is_compatible(parent_np, "eswin,eic7700-clock"))
> + get_parent_base(parent_np);
> + else
> + return;
> +
> + if (IS_ERR(parent_base)) {
> + pr_err("%s: Failed to map registers\n", __func__);
> + goto put_node;
> + }
> +
> + ret = of_property_read_string(np, "clock-output-names", &clk_name);
> + if (ret) {
> + pr_err("%s: Missing clock-output-names\n", __func__);
> + goto put_node;
> + }
> +
> + parent_name = of_clk_get_parent_name(np, 0);
> + if (!parent_name)
> + goto put_node;
> +
> + ret = of_property_read_u32(np, "bit-index", &idx_bit);
> + if (ret) {
> + pr_err("%s: Missing bit-index for gate %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "reg", ®);
> + if (ret) {
> + pr_err("%s: Missing reg for gate %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + clk_hw = clk_hw_register_gate(NULL, clk_name, parent_name,
> + CLK_SET_RATE_PARENT,
> + parent_base + reg, idx_bit, 0, NULL);
> +
> + if (IS_ERR(clk_hw)) {
> + pr_err("%s: Failed to register gate clock %s: %ld\n",
> + __func__, clk_name, PTR_ERR(clk_hw));
> + goto put_node;
> + }
> + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, clk_hw);
> + if (ret) {
> + pr_err("%s: Failed to add clock provider: %d\n", __func__, ret);
> + clk_hw_unregister_gate(clk_hw);
> + }
> +
> +put_node:
> + of_node_put(parent_np);
> +}
> +
> +void __init eswin_clk_mux_register(struct device_node *np)
> +{
> + struct clk *clk;
> + const char *clk_name = NULL;
> + const char **parent_names = NULL;
> + struct device_node *parent_np;
> + u32 shift, width;
> + u32 reg;
> + u8 num_parents;
> + u32 mask = 0;
> + int ret, i;
> +
> + parent_np = of_get_parent(np);
> + if (!parent_np) {
> + pr_err("%s: Failed to get parent node\n", __func__);
> + return;
> + }
> +
> + if (of_device_is_compatible(parent_np, "eswin,eic7700-clock"))
> + get_parent_base(parent_np);
> + else
> + return;
> +
> + if (IS_ERR(parent_base)) {
> + pr_err("%s: Failed to map registers\n", __func__);
> + goto put_node;
> + }
> +
> + ret = of_property_read_string(np, "clock-output-names", &clk_name);
> + if (ret) {
> + pr_err("%s: Missing clock-output-names\n", __func__);
> + goto put_node;
> + }
> +
> + num_parents = of_clk_get_parent_count(np);
> + if (!num_parents) {
> + pr_err("%s: No parents for mux %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + parent_names = kcalloc(num_parents, sizeof(*parent_names),
> + GFP_KERNEL);
> + if (!parent_names)
> + goto put_node;
> +
> + for (i = 0; i < num_parents; i++) {
> + parent_names[i] = of_clk_get_parent_name(np, i);
> + if (!parent_names[i]) {
> + pr_err("%s: Failed to get parent name %d for %s\n",
> + __func__, i, clk_name);
> + goto free_parents;
> + }
> + }
> +
> + ret = of_property_read_u32(np, "shift", &shift);
> + if (ret) {
> + pr_err("%s: Missing shift for mux %s\n", __func__, clk_name);
> + goto free_parents;
> + }
> +
> + ret = of_property_read_u32(np, "width", &width);
> + if (ret) {
> + pr_err("%s: Missing width for mux %s\n", __func__, clk_name);
> + goto free_parents;
> + }
> +
> + ret = of_property_read_u32(np, "reg", ®);
> + if (ret) {
> + pr_err("%s: Missing reg for mux %s\n", __func__, clk_name);
> + goto free_parents;
> + }
> +
> + mask = BIT(width) - 1;
> + clk = clk_register_mux_table(NULL, clk_name, parent_names, num_parents,
> + CLK_SET_RATE_PARENT, parent_base + reg,
> + shift, mask, 0, NULL, NULL);
> +
> + if (IS_ERR(clk)) {
> + pr_err("%s: Failed to register mux clock %s: %ld\n", __func__,
> + clk_name, PTR_ERR(clk));
> + goto free_parents;
> + }
> +
> + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, clk);
> + if (ret) {
> + pr_err("%s: Failed to add clock provider: %d\n", __func__, ret);
> + clk_unregister_mux(clk);
> + }
> +
> +free_parents:
> + kfree(parent_names);
> +put_node:
> + of_node_put(parent_np);
> +}
> +
> +void __init eswin_clk_div_register(struct device_node *np)
> +{
> + struct clk_hw *clk_hw;
> + struct device_node *parent_np;
> + const char *clk_name;
> + const char *parent_name;
> + u32 shift, width, div_flags;
> + u32 reg;
> + int ret;
> +
> + parent_np = of_get_parent(np);
> + if (!parent_np) {
> + pr_err("%s: Failed to get parent node\n", __func__);
> + return;
> + }
> +
> + if (of_device_is_compatible(parent_np, "eswin,eic7700-clock"))
> + get_parent_base(parent_np);
> + else
> + return;
> +
> + if (IS_ERR(parent_base)) {
> + pr_err("%s: Failed to map registers\n", __func__);
> + goto put_node;
> + }
> +
> + ret = of_property_read_string(np, "clock-output-names", &clk_name);
> + if (ret) {
> + pr_err("%s: Missing clock-output-names\n", __func__);
> + goto put_node;
> + }
> +
> + parent_name = of_clk_get_parent_name(np, 0);
> + if (!parent_name) {
> + pr_err("%s: No parent for div %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "shift", &shift);
> + if (ret) {
> + pr_err("%s: Missing shift for div %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "width", &width);
> + if (ret) {
> + pr_err("%s: Missing width for div %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "div-flags", &div_flags);
> + if (ret) {
> + pr_err("%s: Missing div-flags for div %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "reg", ®);
> + if (ret) {
> + pr_err("%s: Missing reg for div %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + clk_hw = clk_hw_register_divider(NULL, clk_name, parent_name, 0,
> + parent_base + reg, shift, width,
> + div_flags, NULL);
> +
> + if (IS_ERR(clk_hw)) {
> + pr_err("%s: Failed to register divider clock %s: %ld\n",
> + __func__, clk_name, PTR_ERR(clk_hw));
> + goto put_node;
> + }
> +
> + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, clk_hw);
> + if (ret) {
> + pr_err("%s: Failed to add clock provider: %d\n", __func__, ret);
> + clk_hw_unregister_divider(clk_hw);
> + }
> +
> +put_node:
> + of_node_put(parent_np);
> +}
> +
> +void __init eswin_clk_pll_register(struct device_node *np)
> +{
> + struct eswin_clk_pll *p_clk = NULL;
> + struct clk *clk = NULL;
> + struct clk_init_data init = {};
> + struct device_node *parent_np;
> + const char *clk_name;
> + const char *parent_name;
> + int ret;
> + u32 reg[4];
> + u32 en_shift, en_width;
> + u32 refdiv_shift, refdiv_width;
> + u32 fbdiv_shift, fbdiv_width;
> + u32 frac_shift, frac_width;
> + u32 postdiv1_shift, postdiv1_width;
> + u32 postdiv2_shift, postdiv2_width;
> + u32 lock_shift, lock_width;
> +
> + parent_np = of_get_parent(np);
> + if (!parent_np) {
> + pr_err("%s: Failed to get parent node\n", __func__);
> + return;
> + }
> +
> + if (of_device_is_compatible(parent_np, "eswin,eic7700-clock"))
> + get_parent_base(parent_np);
> + else
> + return;
> +
> + if (IS_ERR(parent_base)) {
> + pr_err("%s: Failed to map registers\n", __func__);
> + goto put_node;
> + }
> +
> + ret = of_property_read_string(np, "clock-output-names", &clk_name);
> + if (ret) {
> + pr_err("%s: Missing clock-output-names\n", __func__);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "enable-shift", &en_shift);
> + if (ret) {
> + pr_err("%s: Missing enable-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "enable-width", &en_width);
> + if (ret) {
> + pr_err("%s: Missing enable-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "refdiv-shift", &refdiv_shift);
> + if (ret) {
> + pr_err("%s: Missing refdiv-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "refdiv-width", &refdiv_width);
> + if (ret) {
> + pr_err("%s: Missing refdiv-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "fbdiv-shift", &fbdiv_shift);
> + if (ret) {
> + pr_err("%s: Missing fbdiv-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "fbdiv-width", &fbdiv_width);
> + if (ret) {
> + pr_err("%s: Missing fbdiv-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "frac-shift", &frac_shift);
> + if (ret) {
> + pr_err("%s: Missing frac-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "frac-width", &frac_width);
> + if (ret) {
> + pr_err("%s: Missing frac-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "postdiv1-shift", &postdiv1_shift);
> + if (ret) {
> + pr_err("%s: Missing postdiv1-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "postdiv1-width", &postdiv1_width);
> + if (ret) {
> + pr_err("%s: Missing postdiv1-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "postdiv2-shift", &postdiv2_shift);
> + if (ret) {
> + pr_err("%s: Missing postdiv2-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "postdiv2-width", &postdiv2_width);
> + if (ret) {
> + pr_err("%s: Missing postdiv2-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "lock-shift", &lock_shift);
> + if (ret) {
> + pr_err("%s: Missing lock-shift for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32(np, "lock-width", &lock_width);
> + if (ret) {
> + pr_err("%s: Missing lock-width for pll %s\n", __func__,
> + clk_name);
> + goto put_node;
> + }
> +
> + ret = of_property_read_u32_array(np, "reg", reg, 4);
> + if (ret) {
> + pr_err("%s: Missing reg for pll %s\n", __func__, clk_name);
> + goto put_node;
> + }
> +
> + p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL);
> + if (!p_clk)
> + goto put_node;
> +
> + p_clk->ctrl_reg0 = parent_base + reg[0];
> + p_clk->pllen_shift = en_shift;
> + p_clk->pllen_width = en_width;
> + p_clk->refdiv_shift = refdiv_shift;
> + p_clk->refdiv_width = refdiv_width;
> + p_clk->fbdiv_shift = fbdiv_shift;
> + p_clk->fbdiv_width = fbdiv_width;
> +
> + p_clk->ctrl_reg1 = parent_base + reg[1];
> + p_clk->frac_shift = frac_shift;
> + p_clk->frac_width = frac_width;
> +
> + p_clk->ctrl_reg2 = parent_base + reg[2];
> + p_clk->postdiv1_shift = postdiv1_shift;
> + p_clk->postdiv1_width = postdiv1_width;
> + p_clk->postdiv2_shift = postdiv2_shift;
> + p_clk->postdiv2_width = postdiv2_width;
> +
> + p_clk->status_reg = parent_base + reg[3];
> + p_clk->lock_shift = lock_shift;
> + p_clk->lock_width = lock_width;
> +
> + init.name = clk_name;
> + init.flags = 0;
> + init.parent_names = parent_name ? &parent_name : NULL;
> + init.num_parents = parent_name ? 1 : 0;
> + init.ops = &eswin_clk_pll_ops;
> + p_clk->hw.init = &init;
> +
> + clk = clk_register(NULL, &p_clk->hw);
> + if (IS_ERR(clk)) {
> + pr_err("%s: Failed to register pll clock %s: %ld\n", __func__,
> + clk_name, PTR_ERR(clk));
> + kfree(p_clk);
> + goto put_node;
> + }
> +
> + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, clk);
> + if (ret) {
> + pr_err("%s: Failed to add clock provider: %d\n", __func__, ret);
> + clk_unregister(clk);
> + }
> +
> +put_node:
> + of_node_put(parent_np);
> +}
> diff --git a/drivers/clk/eswin/clk.h b/drivers/clk/eswin/clk.h
> new file mode 100644
> index 000000000000..1302540f9e24
> --- /dev/null
> +++ b/drivers/clk/eswin/clk.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd..
> + * All rights reserved.
> + *
> + * Authors:
> + * Yifeng Huang <huangyifeng@...incomputing.com>
> + * Xuyang Dong <dongxuyang@...incomputing.com>
> + */
> +
> +#ifndef __ESWIN_CLK_H__
> +#define __ESWIN_CLK_H__
> +
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +
> +#define CLK_FREQ_1800M 1800000000
> +#define CLK_FREQ_1700M 1700000000
> +#define CLK_FREQ_1600M 1600000000
> +#define CLK_FREQ_1500M 1500000000
> +#define CLK_FREQ_1400M 1400000000
> +#define CLK_FREQ_1300M 1300000000
> +#define CLK_FREQ_1200M 1200000000
> +#define CLK_FREQ_1000M 1000000000
> +#define CLK_FREQ_900M 900000000
> +#define CLK_FREQ_800M 800000000
> +#define CLK_FREQ_700M 700000000
> +#define CLK_FREQ_600M 600000000
> +#define CLK_FREQ_500M 500000000
> +#define CLK_FREQ_400M 400000000
> +#define CLK_FREQ_200M 200000000
> +#define CLK_FREQ_100M 100000000
> +#define CLK_FREQ_24M 24000000
> +
> +#define APLL_HIGH_FREQ 983040000
> +#define APLL_LOW_FREQ 225792000
> +
> +struct eswin_clk_pll {
> + struct clk_hw hw;
> + void __iomem *ctrl_reg0;
> + u8 pllen_shift;
> + u8 pllen_width;
> + u8 refdiv_shift;
> + u8 refdiv_width;
> + u8 fbdiv_shift;
> + u8 fbdiv_width;
> +
> + void __iomem *ctrl_reg1;
> + u8 frac_shift;
> + u8 frac_width;
> +
> + void __iomem *ctrl_reg2;
> + u8 postdiv1_shift;
> + u8 postdiv1_width;
> + u8 postdiv2_shift;
> + u8 postdiv2_width;
> +
> + void __iomem *status_reg;
> + u8 lock_shift;
> + u8 lock_width;
> +};
> +
> +void __init eswin_clk_gate_register(struct device_node *np);
> +void __init eswin_clk_mux_register(struct device_node *np);
> +void __init eswin_clk_div_register(struct device_node *np);
> +void __init eswin_clk_pll_register(struct device_node *np);
> +
> +#endif /* __ESWIN_CLK_H__ */
> --
> 2.17.1
>
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@...ts.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
Powered by blists - more mailing lists