@@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/sifive/Kconfig"
source "drivers/clk/socfpga/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
source "drivers/clk/sprd/Kconfig"
source "drivers/clk/starfive/Kconfig"
source "drivers/clk/sunxi/Kconfig"
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-y += socfpga/
+obj-y += sophgo/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-y += sprd/
obj-$(CONFIG_ARCH_STI) += st/
new file mode 100644
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for SOPHGO SoC family.
+
+config CLK_SOPHGO_CV1800
+ tristate "Support for the Sophgo CV1800 series SoCs clock controller"
+ default m
+ depends on ARCH_SOPHGO || COMPILE_TEST
+ help
+ This driver supports clock controller of Sophgo CV18XX series SoC.
+ The driver require a 25MHz Oscillator to function generate clock.
+ It includes PLLs, common clock function and some vendor clock for
+ IPs of CV18XX series SoC
new file mode 100644
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_SOPHGO_CV1800) += clk-sophgo-cv1800.o
+
+clk-sophgo-cv1800-y += clk-cv1800.o
+clk-sophgo-cv1800-y += clk-cv18xx-common.o
+clk-sophgo-cv1800-y += clk-cv18xx-ip.o
+clk-sophgo-cv1800-y += clk-cv18xx-pll.o
new file mode 100644
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv18xx-common.h"
+
+struct cv1800_clk_ctrl;
+
+struct cv1800_clk_desc {
+ struct clk_hw_onecell_data *clks_data;
+
+ int (*pre_init)(struct device *dev, void __iomem *base,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc);
+};
+
+struct cv1800_clk_ctrl {
+ const struct cv1800_clk_desc *desc;
+ spinlock_t lock;
+};
+
+static int cv1800_clk_init_ctrl(struct device *dev, void __iomem *reg,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc)
+{
+ int i, ret;
+
+ ctrl->desc = desc;
+ spin_lock_init(&ctrl->lock);
+
+ for (i = 0; i < desc->clks_data->num; i++) {
+ struct clk_hw *hw = desc->clks_data->hws[i];
+ struct cv1800_clk_common *common;
+ const char *name;
+
+ if (!hw)
+ continue;
+
+ name = hw->init->name;
+
+ common = hw_to_cv1800_clk_common(hw);
+ common->base = reg;
+ common->lock = &ctrl->lock;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret) {
+ dev_err(dev, "Couldn't register clock %d - %s\n",
+ i, name);
+ return ret;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ desc->clks_data);
+
+ return ret;
+}
+
+static int cv1800_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *reg;
+ int ret;
+ const struct cv1800_clk_desc *desc;
+ struct cv1800_clk_ctrl *ctrl;
+
+ reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ desc = device_get_match_data(dev);
+ if (!desc) {
+ dev_err(dev, "no match data for platform\n");
+ return -EINVAL;
+ }
+
+ ctrl = devm_kmalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ if (desc->pre_init) {
+ ret = desc->pre_init(dev, reg, ctrl, desc);
+ if (ret)
+ return ret;
+ }
+
+ ret = cv1800_clk_init_ctrl(dev, reg, ctrl, desc);
+
+ return ret;
+}
+
+static const struct of_device_id cv1800_clk_ids[] = {
+ { }
+};
+MODULE_DEVICE_TABLE(of, cv1800_clk_ids);
+
+static struct platform_driver cv1800_clk_driver = {
+ .probe = cv1800_clk_probe,
+ .driver = {
+ .name = "cv1800-clk",
+ .suppress_bind_attrs = true,
+ .of_match_table = cv1800_clk_ids,
+ },
+};
+module_platform_driver(cv1800_clk_driver);
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/spinlock.h>
+#include <linux/bug.h>
+
+#include "clk-cv18xx-common.h"
+
+int cv1800_clk_setbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ u32 mask = BIT(field->shift);
+ u32 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(common->lock, flags);
+
+ value = readl(common->base + field->reg);
+ writel(value | mask, common->base + field->reg);
+
+ spin_unlock_irqrestore(common->lock, flags);
+
+ return 0;
+}
+
+int cv1800_clk_clearbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ u32 mask = BIT(field->shift);
+ u32 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(common->lock, flags);
+
+ value = readl(common->base + field->reg);
+ writel(value & ~mask, common->base + field->reg);
+
+ spin_unlock_irqrestore(common->lock, flags);
+
+ return 0;
+}
+
+int cv1800_clk_checkbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ return readl(common->base + field->reg) & BIT(field->shift);
+}
+
+#define PLL_LOCK_TIMEOUT_US (200 * 1000)
+
+void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
+ u32 reg, u32 lock)
+{
+ void __iomem *addr = common->base + reg;
+ u32 regval;
+
+ if (!lock)
+ return;
+
+ WARN_ON(readl_relaxed_poll_timeout(addr, regval, regval & lock,
+ 100, PLL_LOCK_TIMEOUT_US));
+}
new file mode 100644
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#ifndef _CLK_SOPHGO_CV18XX_IP_H_
+#define _CLK_SOPHGO_CV18XX_IP_H_
+
+#include <linux/compiler.h>
+#include <linux/clk-provider.h>
+#include <linux/bitfield.h>
+
+struct cv1800_clk_common {
+ void __iomem *base;
+ spinlock_t *lock;
+ struct clk_hw hw;
+ unsigned long features;
+};
+
+#define CV1800_CLK_COMMON(_name, _parents, _op, _flags) \
+ { \
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \
+ _op, _flags), \
+ }
+
+static inline struct cv1800_clk_common *
+hw_to_cv1800_clk_common(struct clk_hw *hw)
+{
+ return container_of(hw, struct cv1800_clk_common, hw);
+}
+
+struct cv1800_clk_regbit {
+ u16 reg;
+ s8 shift;
+};
+
+struct cv1800_clk_regfield {
+ u16 reg;
+ u8 shift;
+ u8 width;
+ s16 initval;
+ unsigned long flags;
+};
+
+#define CV1800_CLK_BIT(_reg, _shift) \
+ { \
+ .reg = _reg, \
+ .shift = _shift, \
+ }
+
+#define CV1800_CLK_REG(_reg, _shift, _width, _initval, _flags) \
+ { \
+ .reg = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .initval = _initval, \
+ .flags = _flags, \
+ }
+
+#define cv1800_clk_regfield_genmask(_reg) \
+ GENMASK((_reg)->shift + (_reg)->width - 1, (_reg)->shift)
+#define cv1800_clk_regfield_get(_val, _reg) \
+ (((_val) >> (_reg)->shift) & GENMASK((_reg)->width - 1, 0))
+#define cv1800_clk_regfield_set(_val, _new, _reg) \
+ (((_val) & ~cv1800_clk_regfield_genmask((_reg))) | \
+ (((_new) & GENMASK((_reg)->width - 1, 0)) << (_reg)->shift))
+
+#define _CV1800_SET_FIELD(_reg, _val, _field) \
+ (((_reg) & ~(_field)) | FIELD_PREP((_field), (_val)))
+
+int cv1800_clk_setbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+int cv1800_clk_clearbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+int cv1800_clk_checkbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+
+void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
+ u32 reg, u32 lock);
+
+#endif // _CLK_SOPHGO_CV18XX_IP_H_
new file mode 100644
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/gcd.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv18xx-ip.h"
+
+/* GATE */
+const struct clk_ops cv1800_clk_gate_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .recalc_rate = NULL,
+ .round_rate = NULL,
+ .set_rate = NULL,
+};
+
+/* DIV */
+const struct clk_ops cv1800_clk_div_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+};
+
+const struct clk_ops cv1800_clk_bypass_div_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+
+ .set_parent = NULL,
+ .get_parent = NULL,
+};
+
+/* MUX */
+const struct clk_ops cv1800_clk_mux_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+
+ .set_parent = NULL,
+ .get_parent = NULL,
+};
+
+const struct clk_ops cv1800_clk_bypass_mux_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+
+ .set_parent = NULL,
+ .get_parent = NULL,
+};
+
+/* MMUX */
+const struct clk_ops cv1800_clk_mmux_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+
+ .set_parent = NULL,
+ .get_parent = NULL,
+};
+
+/* AUDIO CLK */
+const struct clk_ops cv1800_clk_audio_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .determine_rate = NULL,
+ .recalc_rate = NULL,
+ .set_rate = NULL,
+};
new file mode 100644
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#ifndef _CLK_SOPHGO_CV1800_IP_H_
+#define _CLK_SOPHGO_CV1800_IP_H_
+
+#include "clk-cv18xx-common.h"
+
+struct cv1800_clk_gate {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+};
+
+struct cv1800_clk_div_data {
+ u32 reg;
+ u32 mask;
+ u32 width;
+ u32 init;
+ u32 flags;
+};
+
+struct cv1800_clk_div {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div;
+};
+
+struct cv1800_clk_bypass_div {
+ struct cv1800_clk_div div;
+ struct cv1800_clk_regbit bypass;
+};
+
+struct cv1800_clk_mux {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div;
+ struct cv1800_clk_regfield mux;
+};
+
+struct cv1800_clk_bypass_mux {
+ struct cv1800_clk_mux mux;
+ struct cv1800_clk_regbit bypass;
+};
+
+struct cv1800_clk_mmux {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div[2];
+ struct cv1800_clk_regfield mux[2];
+ struct cv1800_clk_regbit bypass;
+ struct cv1800_clk_regbit clk_sel;
+ const s8 *parent2sel;
+ const u8 *sel2parent[2];
+};
+
+struct cv1800_clk_audio {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit src_en;
+ struct cv1800_clk_regbit output_en;
+ struct cv1800_clk_regbit div_en;
+ struct cv1800_clk_regbit div_up;
+ struct cv1800_clk_regfield m;
+ struct cv1800_clk_regfield n;
+ u32 target_rate;
+};
+
+#define CV1800_GATE(_name, _parent, _gate_reg, _gate_shift, _flags) \
+ struct cv1800_clk_gate _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_gate_ops, \
+ _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift), \
+ }
+
+#define _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(_div_reg, _div_shift, \
+ _div_width, _div_init, \
+ _div_flag), \
+ }
+
+#define _CV1800_FIXED_DIV_FLAG \
+ (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
+
+#define _CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(0, 0, 0, \
+ _fix_div, \
+ _CV1800_FIXED_DIV_FLAG),\
+ }
+
+#define CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _flags) \
+ struct cv1800_clk_div _name = \
+ _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init,\
+ _div_flag, &cv1800_clk_div_ops, _flags)
+
+#define CV1800_BYPASS_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _bypass_reg, _bypass_shift, _flags)\
+ struct cv1800_clk_bypass_div _name = { \
+ .div = _CV1800_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, \
+ _div_width, _div_init, _div_flag, \
+ &cv1800_clk_bypass_div_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _flags) \
+ struct cv1800_clk_div _name = \
+ _CV1800_FIXED_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _fix_div, \
+ &cv1800_clk_div_ops, _flags) \
+
+#define CV1800_BYPASS_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _bypass_reg, _bypass_shift, \
+ _flags) \
+ struct cv1800_clk_bypass_div _name = { \
+ .div = _CV1800_FIXED_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _fix_div, \
+ &cv1800_clk_bypass_div_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(_div_reg, _div_shift, \
+ _div_width, _div_init, \
+ _div_flag), \
+ .mux = CV1800_CLK_REG(_mux_reg, _mux_shift, \
+ _mux_width, 0, 0), \
+ }
+
+#define CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, _flags) \
+ struct cv1800_clk_mux _name = \
+ _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init,\
+ _div_flag, _mux_reg, _mux_shift, _mux_width,\
+ &cv1800_clk_mux_ops, _flags)
+
+#define CV1800_BYPASS_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ _bypass_reg, _bypass_shift, _flags) \
+ struct cv1800_clk_bypass_mux _name = { \
+ .mux = _CV1800_MUX(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, \
+ _div_init, _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ &cv1800_clk_bypass_mux_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define CV1800_MMUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div0_reg, _div0_shift, _div0_width, _div0_init, \
+ _div0_flag, \
+ _div1_reg, _div1_shift, _div1_width, _div1_init, \
+ _div1_flag, \
+ _mux0_reg, _mux0_shift, _mux0_width, \
+ _mux1_reg, _mux1_shift, _mux1_width, \
+ _bypass_reg, _bypass_shift, \
+ _clk_sel_reg, _clk_sel_shift, \
+ _parent2sel, _sel2parent0, _sel2parent1, _flags) \
+ struct cv1800_clk_mmux _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_mmux_ops,\
+ _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift),\
+ .div = { \
+ CV1800_CLK_REG(_div0_reg, _div0_shift, \
+ _div0_width, _div0_init, \
+ _div0_flag), \
+ CV1800_CLK_REG(_div1_reg, _div1_shift, \
+ _div1_width, _div1_init, \
+ _div1_flag), \
+ }, \
+ .mux = { \
+ CV1800_CLK_REG(_mux0_reg, _mux0_shift, \
+ _mux0_width, 0, 0), \
+ CV1800_CLK_REG(_mux1_reg, _mux1_shift, \
+ _mux1_width, 0, 0), \
+ }, \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, \
+ _bypass_shift), \
+ .clk_sel = CV1800_CLK_BIT(_clk_sel_reg, \
+ _clk_sel_shift), \
+ .parent2sel = _parent2sel, \
+ .sel2parent = { _sel2parent0, _sel2parent1 }, \
+ }
+
+#define CV1800_ACLK(_name, _parent, \
+ _src_en_reg, _src_en_reg_shift, \
+ _output_en_reg, _output_en_shift, \
+ _div_en_reg, _div_en_reg_shift, \
+ _div_up_reg, _div_up_reg_shift, \
+ _m_reg, _m_shift, _m_width, _m_flag, \
+ _n_reg, _n_shift, _n_width, _n_flag, \
+ _target_rate, _flags) \
+ struct cv1800_clk_audio _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_audio_ops,\
+ _flags), \
+ .src_en = CV1800_CLK_BIT(_src_en_reg, \
+ _src_en_reg_shift), \
+ .output_en = CV1800_CLK_BIT(_output_en_reg, \
+ _output_en_shift), \
+ .div_en = CV1800_CLK_BIT(_div_en_reg, \
+ _div_en_reg_shift), \
+ .div_up = CV1800_CLK_BIT(_div_up_reg, \
+ _div_up_reg_shift), \
+ .m = CV1800_CLK_REG(_m_reg, _m_shift, \
+ _m_width, 0, _m_flag), \
+ .n = CV1800_CLK_REG(_n_reg, _n_shift, \
+ _n_width, 0, _n_flag), \
+ .target_rate = _target_rate, \
+ }
+
+extern const struct clk_ops cv1800_clk_gate_ops;
+extern const struct clk_ops cv1800_clk_div_ops;
+extern const struct clk_ops cv1800_clk_bypass_div_ops;
+extern const struct clk_ops cv1800_clk_mux_ops;
+extern const struct clk_ops cv1800_clk_bypass_mux_ops;
+extern const struct clk_ops cv1800_clk_mmux_ops;
+extern const struct clk_ops cv1800_clk_audio_ops;
+
+#endif // _CLK_SOPHGO_CV1800_IP_H_
new file mode 100644
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv18xx-pll.h"
+
+const struct clk_ops cv1800_clk_ipll_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .recalc_rate = NULL,
+ .determine_rate = NULL,
+ .set_rate = NULL,
+};
+
+const struct clk_ops cv1800_clk_fpll_ops = {
+ .disable = NULL,
+ .enable = NULL,
+ .is_enabled = NULL,
+
+ .recalc_rate = NULL,
+ .determine_rate = NULL,
+ .set_rate = NULL,
+
+ .set_parent = NULL,
+ .get_parent = NULL,
+};
new file mode 100644
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
+ */
+
+#ifndef _CLK_SOPHGO_CV1800_PLL_H_
+#define _CLK_SOPHGO_CV1800_PLL_H_
+
+#include "clk-cv18xx-common.h"
+
+struct cv1800_clk_pll_limit {
+ struct {
+ u8 min;
+ u8 max;
+ } pre_div, div, post_div, ictrl, mode;
+};
+
+#define _CV1800_PLL_LIMIT(_min, _max) \
+ { \
+ .min = _min, \
+ .max = _max, \
+ } \
+
+#define for_each_pll_limit_range(_var, _restrict) \
+ for (_var = (_restrict)->min; _var <= (_restrict)->max; _var++)
+
+struct cv1800_clk_pll_synthesizer {
+ struct cv1800_clk_regbit en;
+ struct cv1800_clk_regbit clk_half;
+ u32 ctrl;
+ u32 set;
+};
+
+#define _PLL_PRE_DIV_SEL_FIELD GENMASK(6, 0)
+#define _PLL_POST_DIV_SEL_FIELD GENMASK(14, 8)
+#define _PLL_SEL_MODE_FIELD GENMASK(16, 15)
+#define _PLL_DIV_SEL_FIELD GENMASK(23, 17)
+#define _PLL_ICTRL_FIELD GENMASK(26, 24)
+
+#define _PLL_ALL_FIELD_MASK \
+ (_PLL_PRE_DIV_SEL_FIELD | \
+ _PLL_POST_DIV_SEL_FIELD | \
+ _PLL_SEL_MODE_FIELD | \
+ _PLL_DIV_SEL_FIELD | \
+ _PLL_ICTRL_FIELD)
+
+#define PLL_COPY_REG(_dest, _src) \
+ (((_dest) & (~_PLL_ALL_FIELD_MASK)) | ((_src) & _PLL_ALL_FIELD_MASK))
+
+#define PLL_GET_PRE_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_PRE_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_POST_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_POST_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_SEL_MODE(_reg) \
+ FIELD_GET(_PLL_SEL_MODE_FIELD, (_reg))
+#define PLL_GET_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_ICTRL(_reg) \
+ FIELD_GET(_PLL_ICTRL_FIELD, (_reg))
+
+#define PLL_SET_PRE_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_PRE_DIV_SEL_FIELD)
+#define PLL_SET_POST_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_POST_DIV_SEL_FIELD)
+#define PLL_SET_SEL_MODE(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_SEL_MODE_FIELD)
+#define PLL_SET_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_DIV_SEL_FIELD)
+#define PLL_SET_ICTRL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_ICTRL_FIELD)
+
+struct cv1800_clk_pll {
+ struct cv1800_clk_common common;
+ u32 pll_reg;
+ struct cv1800_clk_regbit pll_pwd;
+ struct cv1800_clk_regbit pll_status;
+ const struct cv1800_clk_pll_limit *pll_limit;
+ struct cv1800_clk_pll_synthesizer *pll_syn;
+};
+
+#define CV1800_INTEGRAL_PLL(_name, _parent, _pll_reg, \
+ _pll_pwd_reg, _pll_pwd_shift, \
+ _pll_status_reg, _pll_status_shift, \
+ _pll_limit, _flags) \
+ struct cv1800_clk_pll _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_ipll_ops,\
+ _flags), \
+ .pll_reg = _pll_reg, \
+ .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
+ _pll_pwd_shift), \
+ .pll_status = CV1800_CLK_BIT(_pll_status_reg, \
+ _pll_status_shift), \
+ .pll_limit = _pll_limit, \
+ .pll_syn = NULL, \
+ }
+
+#define CV1800_FACTIONAL_PLL(_name, _parent, _pll_reg, \
+ _pll_pwd_reg, _pll_pwd_shift, \
+ _pll_status_reg, _pll_status_shift, \
+ _pll_limit, _pll_syn, _flags) \
+ struct cv1800_clk_pll _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_fpll_ops,\
+ _flags), \
+ .pll_reg = _pll_reg, \
+ .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
+ _pll_pwd_shift), \
+ .pll_status = CV1800_CLK_BIT(_pll_status_reg, \
+ _pll_status_shift), \
+ .pll_limit = _pll_limit, \
+ .pll_syn = _pll_syn, \
+ }
+
+extern const struct clk_ops cv1800_clk_ipll_ops;
+extern const struct clk_ops cv1800_clk_fpll_ops;
+
+#endif // _CLK_SOPHGO_CV1800_PLL_H_
Add driver skeleton for CV1800/SG2000 series clock controller. The skeleton code includes: 1. common utility code for clk_ops implementation 2. basic probe code of the whole driver 3. helper structure for clk definition Signed-off-by: Inochi Amaoto <inochiama@outlook.com> --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/sophgo/Kconfig | 12 ++ drivers/clk/sophgo/Makefile | 7 + drivers/clk/sophgo/clk-cv1800.c | 113 +++++++++++ drivers/clk/sophgo/clk-cv18xx-common.c | 66 +++++++ drivers/clk/sophgo/clk-cv18xx-common.h | 81 ++++++++ drivers/clk/sophgo/clk-cv18xx-ip.c | 98 ++++++++++ drivers/clk/sophgo/clk-cv18xx-ip.h | 261 +++++++++++++++++++++++++ drivers/clk/sophgo/clk-cv18xx-pll.c | 34 ++++ drivers/clk/sophgo/clk-cv18xx-pll.h | 118 +++++++++++ 11 files changed, 792 insertions(+) create mode 100644 drivers/clk/sophgo/Kconfig create mode 100644 drivers/clk/sophgo/Makefile create mode 100644 drivers/clk/sophgo/clk-cv1800.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h -- 2.43.0