[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20250815093720.1088-1-dongxuyang@eswincomputing.com>
Date: Fri, 15 Aug 2025 17:37:20 +0800
From: dongxuyang@...incomputing.com
To: 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,
Xuyang Dong <dongxuyang@...incomputing.com>
Subject: [PATCH v4 2/3] clock: eswin: Add eic7700 clock driver
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.
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
+ 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,
+ 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
+
+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
Powered by blists - more mailing lists