summaryrefslogtreecommitdiffstats
path: root/drivers/clk/thead
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/clk/thead
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/clk/thead')
-rw-r--r--drivers/clk/thead/Kconfig13
-rw-r--r--drivers/clk/thead/Makefile2
-rw-r--r--drivers/clk/thead/clk-th1520-ap.c1644
3 files changed, 1659 insertions, 0 deletions
diff --git a/drivers/clk/thead/Kconfig b/drivers/clk/thead/Kconfig
new file mode 100644
index 000000000..95e0d9eb9
--- /dev/null
+++ b/drivers/clk/thead/Kconfig
@@ -0,0 +1,13 @@
+#SPDX-License-Identifier: GPL-2.0
+
+config CLK_THEAD_TH1520_AP
+ bool "T-HEAD TH1520 AP clock support"
+ depends on ARCH_THEAD || COMPILE_TEST
+ depends on 64BIT
+ default ARCH_THEAD
+ select REGMAP_MMIO
+ help
+ Say yes here to support the AP sub system clock controller
+ on the T-HEAD TH1520 SoC. This includes configuration of
+ both CPU PLLs, both DPU PLLs as well as the GMAC, VIDEO,
+ and TEE PLLs.
diff --git a/drivers/clk/thead/Makefile b/drivers/clk/thead/Makefile
new file mode 100644
index 000000000..7ee0bec1f
--- /dev/null
+++ b/drivers/clk/thead/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_THEAD_TH1520_AP) += clk-th1520-ap.o
diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c
new file mode 100644
index 000000000..3a6847f1c
--- /dev/null
+++ b/drivers/clk/thead/clk-th1520-ap.c
@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ * Copyright (C) 2023 Vivo Communication Technology Co. Ltd.
+ * Authors: Yangtao Li <frank.li@vivo.com>
+ */
+
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define TH1520_PLL_STS 0x80
+
+#define TH1520_PLL_POSTDIV2 GENMASK(26, 24)
+#define TH1520_PLL_POSTDIV1 GENMASK(22, 20)
+#define TH1520_PLL_FBDIV GENMASK(19, 8)
+#define TH1520_PLL_REFDIV GENMASK(5, 0)
+#define TH1520_PLL_BYPASS BIT(30)
+#define TH1520_PLL_VCO_RST BIT(29)
+#define TH1520_PLL_DACPD BIT(25)
+#define TH1520_PLL_DSMPD BIT(24)
+#define TH1520_PLL_FRAC GENMASK(23, 0)
+#define TH1520_PLL_FRAC_BITS 24
+
+/*
+ * All PLLs in TH1520 take 21250ns at maximum to lock, let's take its double
+ * for safety.
+ */
+#define TH1520_PLL_LOCK_TIMEOUT_US 44
+#define TH1520_PLL_STABLE_DELAY_US 30
+
+/* c910_bus_clk must be kept below 750MHz for stability */
+#define TH1520_C910_BUS_MAX_RATE (750 * 1000 * 1000)
+
+struct ccu_internal {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_div_internal {
+ u8 shift;
+ u8 width;
+ u32 flags;
+};
+
+struct ccu_common {
+ int clkid;
+ struct regmap *map;
+ u16 cfg0;
+ u16 cfg1;
+ struct clk_hw hw;
+};
+
+struct ccu_mux {
+ int clkid;
+ u32 reg;
+ struct clk_mux mux;
+};
+
+struct ccu_gate {
+ int clkid;
+ u32 reg;
+ struct clk_gate gate;
+};
+
+struct ccu_div {
+ u32 enable;
+ u32 div_en;
+ struct ccu_div_internal div;
+ struct ccu_internal mux;
+ struct ccu_common common;
+};
+
+struct ccu_pll_cfg {
+ unsigned long freq;
+ u32 fbdiv;
+ u32 frac;
+ u32 postdiv1;
+ u32 postdiv2;
+};
+
+struct ccu_pll {
+ struct ccu_common common;
+ u32 lock_sts_mask;
+ int cfgnum;
+ const struct ccu_pll_cfg *cfgs;
+};
+
+#define TH_CCU_ARG(_shift, _width) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ }
+
+#define TH_CCU_DIV_FLAGS(_shift, _width, _flags) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ .flags = _flags, \
+ }
+
+#define TH_CCU_MUX_FLAGS(_name, _parents, _shift, _width, _flags, \
+ _mux_flags) \
+ { \
+ .mask = GENMASK(_width - 1, 0), \
+ .shift = _shift, \
+ .flags = _mux_flags, \
+ .hw.init = CLK_HW_INIT_PARENTS_DATA( \
+ _name, \
+ _parents, \
+ &clk_mux_ops, \
+ _flags), \
+ }
+
+#define TH_CCU_MUX(_name, _parents, _shift, _width) \
+ TH_CCU_MUX_FLAGS(_name, _parents, _shift, _width, 0, 0)
+
+#define CCU_GATE(_clkid, _struct, _name, _parent, _reg, _bit, _flags) \
+ struct ccu_gate _struct = { \
+ .clkid = _clkid, \
+ .reg = _reg, \
+ .gate = { \
+ .bit_idx = _bit, \
+ .hw.init = CLK_HW_INIT_PARENTS_DATA( \
+ _name, \
+ _parent, \
+ &clk_gate_ops, \
+ _flags), \
+ } \
+ }
+
+static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
+{
+ return container_of(hw, struct ccu_common, hw);
+}
+
+static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_pll, common);
+}
+
+static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_div, common);
+}
+
+static u8 ccu_get_parent_helper(struct ccu_common *common,
+ struct ccu_internal *mux)
+{
+ unsigned int val;
+ u8 parent;
+
+ regmap_read(common->map, common->cfg0, &val);
+ parent = val >> mux->shift;
+ parent &= GENMASK(mux->width - 1, 0);
+
+ return parent;
+}
+
+static int ccu_set_parent_helper(struct ccu_common *common,
+ struct ccu_internal *mux,
+ u8 index)
+{
+ return regmap_update_bits(common->map, common->cfg0,
+ GENMASK(mux->width - 1, 0) << mux->shift,
+ index << mux->shift);
+}
+
+static void ccu_disable_helper(struct ccu_common *common, u32 gate)
+{
+ if (!gate)
+ return;
+ regmap_update_bits(common->map, common->cfg0,
+ gate, ~gate);
+}
+
+static int ccu_enable_helper(struct ccu_common *common, u32 gate)
+{
+ unsigned int val;
+ int ret;
+
+ if (!gate)
+ return 0;
+
+ ret = regmap_update_bits(common->map, common->cfg0, gate, gate);
+ regmap_read(common->map, common->cfg0, &val);
+ return ret;
+}
+
+static int ccu_is_enabled_helper(struct ccu_common *common, u32 gate)
+{
+ unsigned int val;
+
+ if (!gate)
+ return true;
+
+ regmap_read(common->map, common->cfg0, &val);
+ return val & gate;
+}
+
+static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+ unsigned long rate;
+ unsigned int val;
+
+ regmap_read(cd->common.map, cd->common.cfg0, &val);
+ val = val >> cd->div.shift;
+ val &= GENMASK(cd->div.width - 1, 0);
+ rate = divider_recalc_rate(hw, parent_rate, val, NULL,
+ cd->div.flags, cd->div.width);
+
+ return rate;
+}
+
+static int ccu_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+ unsigned int val;
+
+ if (cd->div_en)
+ return divider_determine_rate(hw, req, NULL,
+ cd->div.width, cd->div.flags);
+
+ regmap_read(cd->common.map, cd->common.cfg0, &val);
+ val = val >> cd->div.shift;
+ val &= GENMASK(cd->div.width - 1, 0);
+ return divider_ro_determine_rate(hw, req, NULL, cd->div.width,
+ cd->div.flags, val);
+}
+
+static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+ int val = divider_get_val(rate, parent_rate, NULL,
+ cd->div.width, cd->div.flags);
+ unsigned int curr_val, reg_val;
+
+ if (val < 0)
+ return val;
+
+ regmap_read(cd->common.map, cd->common.cfg0, &reg_val);
+ curr_val = reg_val >> cd->div.shift;
+ curr_val &= GENMASK(cd->div.width - 1, 0);
+
+ if (!cd->div_en && curr_val != val)
+ return -EINVAL;
+
+ reg_val &= ~cd->div_en;
+ regmap_write(cd->common.map, cd->common.cfg0, reg_val);
+ udelay(1);
+
+ reg_val &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
+ reg_val |= val << cd->div.shift;
+ regmap_write(cd->common.map, cd->common.cfg0, reg_val);
+
+ reg_val |= cd->div_en;
+ regmap_write(cd->common.map, cd->common.cfg0, reg_val);
+
+ return 0;
+}
+
+static u8 ccu_div_get_parent(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_get_parent_helper(&cd->common, &cd->mux);
+}
+
+static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_set_parent_helper(&cd->common, &cd->mux, index);
+}
+
+static void ccu_div_disable(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ ccu_disable_helper(&cd->common, cd->enable);
+}
+
+static int ccu_div_enable(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_enable_helper(&cd->common, cd->enable);
+}
+
+static int ccu_div_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_is_enabled_helper(&cd->common, cd->enable);
+}
+
+static const struct clk_ops ccu_div_ops = {
+ .disable = ccu_div_disable,
+ .enable = ccu_div_enable,
+ .is_enabled = ccu_div_is_enabled,
+ .get_parent = ccu_div_get_parent,
+ .set_parent = ccu_div_set_parent,
+ .recalc_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_div_set_rate,
+ .determine_rate = ccu_div_determine_rate,
+};
+
+static void ccu_pll_disable(struct clk_hw *hw)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+
+ regmap_set_bits(pll->common.map, pll->common.cfg1,
+ TH1520_PLL_VCO_RST);
+}
+
+static int ccu_pll_enable(struct clk_hw *hw)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+ u32 reg;
+ int ret;
+
+ regmap_clear_bits(pll->common.map, pll->common.cfg1,
+ TH1520_PLL_VCO_RST);
+
+ ret = regmap_read_poll_timeout_atomic(pll->common.map, TH1520_PLL_STS,
+ reg, reg & pll->lock_sts_mask,
+ 5, TH1520_PLL_LOCK_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ udelay(TH1520_PLL_STABLE_DELAY_US);
+
+ return 0;
+}
+
+static int ccu_pll_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+
+ return !regmap_test_bits(pll->common.map, pll->common.cfg1,
+ TH1520_PLL_VCO_RST);
+}
+
+static unsigned long th1520_pll_vco_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+ unsigned long div, mul, frac;
+ unsigned int cfg0, cfg1;
+ u64 rate = parent_rate;
+
+ regmap_read(pll->common.map, pll->common.cfg0, &cfg0);
+ regmap_read(pll->common.map, pll->common.cfg1, &cfg1);
+
+ mul = FIELD_GET(TH1520_PLL_FBDIV, cfg0);
+ div = FIELD_GET(TH1520_PLL_REFDIV, cfg0);
+ if (!(cfg1 & TH1520_PLL_DSMPD)) {
+ mul <<= TH1520_PLL_FRAC_BITS;
+ frac = FIELD_GET(TH1520_PLL_FRAC, cfg1);
+ mul += frac;
+ div <<= TH1520_PLL_FRAC_BITS;
+ }
+ rate = parent_rate * mul;
+ rate = rate / div;
+ return rate;
+}
+
+static unsigned long th1520_pll_postdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+ unsigned long div, rate = parent_rate;
+ unsigned int cfg0, cfg1;
+
+ regmap_read(pll->common.map, pll->common.cfg0, &cfg0);
+ regmap_read(pll->common.map, pll->common.cfg1, &cfg1);
+
+ if (cfg1 & TH1520_PLL_BYPASS)
+ return rate;
+
+ div = FIELD_GET(TH1520_PLL_POSTDIV1, cfg0) *
+ FIELD_GET(TH1520_PLL_POSTDIV2, cfg0);
+
+ rate = rate / div;
+
+ return rate;
+}
+
+static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long rate = parent_rate;
+
+ rate = th1520_pll_vco_recalc_rate(hw, rate);
+ rate = th1520_pll_postdiv_recalc_rate(hw, rate);
+
+ return rate;
+}
+
+static const struct ccu_pll_cfg *ccu_pll_lookup_best_cfg(struct ccu_pll *pll,
+ unsigned long rate)
+{
+ unsigned long best_delta = ULONG_MAX;
+ const struct ccu_pll_cfg *best_cfg;
+ int i;
+
+ for (i = 0; i < pll->cfgnum; i++) {
+ const struct ccu_pll_cfg *cfg = &pll->cfgs[i];
+ unsigned long delta;
+
+ delta = abs_diff(cfg->freq, rate);
+ if (delta < best_delta) {
+ best_delta = delta;
+ best_cfg = cfg;
+ }
+ }
+
+ return best_cfg;
+}
+
+static int ccu_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+
+ req->rate = ccu_pll_lookup_best_cfg(pll, req->rate)->freq;
+
+ return 0;
+}
+
+static int ccu_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ccu_pll *pll = hw_to_ccu_pll(hw);
+ const struct ccu_pll_cfg *cfg;
+
+ cfg = ccu_pll_lookup_best_cfg(pll, rate);
+
+ ccu_pll_disable(hw);
+
+ regmap_write(pll->common.map, pll->common.cfg0,
+ FIELD_PREP(TH1520_PLL_REFDIV, 1) |
+ FIELD_PREP(TH1520_PLL_FBDIV, cfg->fbdiv) |
+ FIELD_PREP(TH1520_PLL_POSTDIV1, cfg->postdiv1) |
+ FIELD_PREP(TH1520_PLL_POSTDIV2, cfg->postdiv2));
+
+ regmap_update_bits(pll->common.map, pll->common.cfg1,
+ TH1520_PLL_DACPD | TH1520_PLL_DSMPD |
+ TH1520_PLL_FRAC,
+ cfg->frac ? cfg->frac :
+ TH1520_PLL_DACPD | TH1520_PLL_DSMPD);
+
+ return ccu_pll_enable(hw);
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .disable = ccu_pll_disable,
+ .enable = ccu_pll_enable,
+ .is_enabled = ccu_pll_is_enabled,
+ .recalc_rate = ccu_pll_recalc_rate,
+ .determine_rate = ccu_pll_determine_rate,
+ .set_rate = ccu_pll_set_rate,
+};
+
+/*
+ * c910_clk could be reparented glitchlessly for DVFS. There are two parents,
+ * - c910_i0_clk, derived from cpu_pll0_clk or osc_24m.
+ * - cpu_pll1_clk, which provides the exact same set of rates as cpu_pll0_clk.
+ *
+ * During rate setting, always forward the request to the unused parent, and
+ * then switch c910_clk to it to avoid glitch.
+ */
+static u8 c910_clk_get_parent(struct clk_hw *hw)
+{
+ return clk_mux_ops.get_parent(hw);
+}
+
+static int c910_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ return clk_mux_ops.set_parent(hw, index);
+}
+
+static unsigned long c910_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate;
+}
+
+static int c910_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ u8 alt_parent_index = !c910_clk_get_parent(hw);
+ struct clk_hw *alt_parent;
+
+ alt_parent = clk_hw_get_parent_by_index(hw, alt_parent_index);
+
+ req->rate = clk_hw_round_rate(alt_parent, req->rate);
+ req->best_parent_hw = alt_parent;
+ req->best_parent_rate = req->rate;
+
+ return 0;
+}
+
+static int c910_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return -EOPNOTSUPP;
+}
+
+static int c910_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate, u8 index)
+{
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, index);
+
+ clk_set_rate(parent->clk, parent_rate);
+
+ c910_clk_set_parent(hw, index);
+
+ return 0;
+}
+
+static const struct clk_ops c910_clk_ops = {
+ .get_parent = c910_clk_get_parent,
+ .set_parent = c910_clk_set_parent,
+ .recalc_rate = c910_clk_recalc_rate,
+ .determine_rate = c910_clk_determine_rate,
+ .set_rate = c910_clk_set_rate,
+ .set_rate_and_parent = c910_clk_set_rate_and_parent,
+};
+
+static const struct clk_parent_data osc_24m_clk[] = {
+ { .index = 0 }
+};
+
+static const struct ccu_pll_cfg cpu_pll_cfgs[] = {
+ { 125000000, 125, 0, 6, 4 },
+ { 200000000, 125, 0, 5, 3 },
+ { 300000000, 125, 0, 5, 2 },
+ { 400000000, 100, 0, 3, 2 },
+ { 500000000, 125, 0, 6, 1 },
+ { 600000000, 125, 0, 5, 1 },
+ { 702000000, 117, 0, 4, 1 },
+ { 800000000, 100, 0, 3, 1 },
+ { 900000000, 75, 0, 2, 1 },
+ { 1000000000, 125, 0, 3, 1 },
+ { 1104000000, 92, 0, 2, 1 },
+ { 1200000000, 100, 0, 2, 1 },
+ { 1296000000, 108, 0, 2, 1 },
+ { 1404000000, 117, 0, 2, 1 },
+ { 1500000000, 125, 0, 2, 1 },
+ { 1608000000, 67, 0, 1, 1 },
+ { 1704000000, 71, 0, 1, 1 },
+ { 1800000000, 75, 0, 1, 1 },
+ { 1896000000, 79, 0, 1, 1 },
+ { 1992000000, 83, 0, 1, 1 },
+ { 2112000000, 88, 0, 1, 1 },
+ { 2208000000, 92, 0, 1, 1 },
+ { 2304000000, 96, 0, 1, 1 },
+ { 2400000000, 100, 0, 1, 1 },
+};
+
+static struct ccu_pll cpu_pll0_clk = {
+ .common = {
+ .clkid = CLK_CPU_PLL0,
+ .cfg0 = 0x000,
+ .cfg1 = 0x004,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("cpu-pll0",
+ osc_24m_clk,
+ &clk_pll_ops,
+ CLK_IS_CRITICAL),
+ },
+ .lock_sts_mask = BIT(1),
+ .cfgnum = ARRAY_SIZE(cpu_pll_cfgs),
+ .cfgs = cpu_pll_cfgs,
+};
+
+static struct ccu_pll cpu_pll1_clk = {
+ .common = {
+ .clkid = CLK_CPU_PLL1,
+ .cfg0 = 0x010,
+ .cfg1 = 0x014,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("cpu-pll1",
+ osc_24m_clk,
+ &clk_pll_ops,
+ CLK_IS_CRITICAL),
+ },
+ .lock_sts_mask = BIT(4),
+ .cfgnum = ARRAY_SIZE(cpu_pll_cfgs),
+ .cfgs = cpu_pll_cfgs,
+};
+
+static const struct ccu_pll_cfg gmac_pll_cfg = {
+ .freq = 1000000000,
+ .fbdiv = 125,
+ .frac = 0,
+ .postdiv1 = 3,
+ .postdiv2 = 1,
+};
+
+static struct ccu_pll gmac_pll_clk = {
+ .common = {
+ .clkid = CLK_GMAC_PLL,
+ .cfg0 = 0x020,
+ .cfg1 = 0x024,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("gmac-pll",
+ osc_24m_clk,
+ &clk_pll_ops,
+ CLK_IS_CRITICAL),
+ },
+ .lock_sts_mask = BIT(3),
+ .cfgnum = 1,
+ .cfgs = &gmac_pll_cfg,
+};
+
+static const struct clk_hw *gmac_pll_clk_parent[] = {
+ &gmac_pll_clk.common.hw
+};
+
+static const struct clk_parent_data gmac_pll_clk_pd[] = {
+ { .hw = &gmac_pll_clk.common.hw }
+};
+
+static const struct ccu_pll_cfg video_pll_cfg = {
+ .freq = 792000000,
+ .fbdiv = 99,
+ .frac = 0,
+ .postdiv1 = 3,
+ .postdiv2 = 1,
+};
+
+static struct ccu_pll video_pll_clk = {
+ .common = {
+ .clkid = CLK_VIDEO_PLL,
+ .cfg0 = 0x030,
+ .cfg1 = 0x034,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("video-pll",
+ osc_24m_clk,
+ &clk_pll_ops,
+ CLK_IS_CRITICAL),
+ },
+ .lock_sts_mask = BIT(7),
+ .cfgnum = 1,
+ .cfgs = &video_pll_cfg,
+};
+
+static const struct clk_hw *video_pll_clk_parent[] = {
+ &video_pll_clk.common.hw
+};
+
+static const struct clk_parent_data video_pll_clk_pd[] = {
+ { .hw = &video_pll_clk.common.hw }
+};
+
+static const struct ccu_pll_cfg dpu_pll_cfg = {
+ .freq = 1188000000,
+ .fbdiv = 99,
+ .frac = 0,
+ .postdiv1 = 2,
+ .postdiv2 = 1,
+};
+
+static struct ccu_pll dpu0_pll_clk = {
+ .common = {
+ .clkid = CLK_DPU0_PLL,
+ .cfg0 = 0x040,
+ .cfg1 = 0x044,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("dpu0-pll",
+ osc_24m_clk,
+ &clk_pll_ops,
+ 0),
+ },
+ .lock_sts_mask = BIT(8),
+ .cfgnum = 1,
+ .cfgs = &dpu_pll_cfg,
+};
+
+static const struct clk_hw *dpu0_pll_clk_parent[] = {
+ &dpu0_pll_clk.common.hw
+};
+
+static struct ccu_pll dpu1_pll_clk = {
+ .common = {
+ .clkid = CLK_DPU1_PLL,
+ .cfg0 = 0x050,
+ .cfg1 = 0x054,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("dpu1-pll",
+ osc_24m_clk,
+ &clk_pll_ops,
+ 0),
+ },
+ .lock_sts_mask = BIT(9),
+ .cfgnum = 1,
+ .cfgs = &dpu_pll_cfg,
+};
+
+static const struct clk_hw *dpu1_pll_clk_parent[] = {
+ &dpu1_pll_clk.common.hw
+};
+
+static const struct ccu_pll_cfg tee_pll_cfg = {
+ .freq = 792000000,
+ .fbdiv = 99,
+ .frac = 0,
+ .postdiv1 = 3,
+ .postdiv2 = 1,
+};
+
+static struct ccu_pll tee_pll_clk = {
+ .common = {
+ .clkid = CLK_TEE_PLL,
+ .cfg0 = 0x060,
+ .cfg1 = 0x064,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("tee-pll",
+ osc_24m_clk,
+ &clk_pll_ops,
+ CLK_IS_CRITICAL),
+ },
+ .lock_sts_mask = BIT(10),
+ .cfgnum = 1,
+ .cfgs = &tee_pll_cfg,
+};
+
+static const struct clk_parent_data c910_i0_parents[] = {
+ { .hw = &cpu_pll0_clk.common.hw },
+ { .index = 0 }
+};
+
+static struct ccu_mux c910_i0_clk = {
+ .clkid = CLK_C910_I0,
+ .reg = 0x100,
+ .mux = TH_CCU_MUX_FLAGS("c910-i0", c910_i0_parents, 1, 1,
+ CLK_SET_RATE_PARENT, CLK_MUX_ROUND_CLOSEST),
+};
+
+static const struct clk_parent_data c910_parents[] = {
+ { .hw = &c910_i0_clk.mux.hw },
+ { .hw = &cpu_pll1_clk.common.hw }
+};
+
+static struct ccu_mux c910_clk = {
+ .clkid = CLK_C910,
+ .reg = 0x100,
+ .mux = {
+ .mask = BIT(0),
+ .shift = 0,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("c910",
+ c910_parents,
+ &c910_clk_ops,
+ CLK_SET_RATE_PARENT),
+ },
+};
+
+static struct ccu_div c910_bus_clk = {
+ .enable = BIT(7),
+ .div_en = BIT(11),
+ .div = TH_CCU_DIV_FLAGS(8, 3, 0),
+ .common = {
+ .clkid = CLK_C910_BUS,
+ .cfg0 = 0x100,
+ .hw.init = CLK_HW_INIT_HW("c910-bus",
+ &c910_clk.mux.hw,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static const struct clk_parent_data ahb2_cpusys_parents[] = {
+ { .hw = &gmac_pll_clk.common.hw },
+ { .index = 0 }
+};
+
+static struct ccu_div ahb2_cpusys_hclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_AHB2_CPUSYS_HCLK,
+ .cfg0 = 0x120,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("ahb2-cpusys-hclk",
+ ahb2_cpusys_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data ahb2_cpusys_hclk_pd[] = {
+ { .hw = &ahb2_cpusys_hclk.common.hw }
+};
+
+static const struct clk_hw *ahb2_cpusys_hclk_parent[] = {
+ &ahb2_cpusys_hclk.common.hw,
+};
+
+static struct ccu_div apb3_cpusys_pclk = {
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .clkid = CLK_APB3_CPUSYS_PCLK,
+ .cfg0 = 0x130,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("apb3-cpusys-pclk",
+ ahb2_cpusys_hclk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data apb3_cpusys_pclk_pd[] = {
+ { .hw = &apb3_cpusys_pclk.common.hw }
+};
+
+static struct ccu_div axi4_cpusys2_aclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_AXI4_CPUSYS2_ACLK,
+ .cfg0 = 0x134,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("axi4-cpusys2-aclk",
+ gmac_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static const struct clk_parent_data axi4_cpusys2_aclk_pd[] = {
+ { .hw = &axi4_cpusys2_aclk.common.hw }
+};
+
+static const struct clk_parent_data axi_parents[] = {
+ { .hw = &video_pll_clk.common.hw },
+ { .index = 0 }
+};
+
+static struct ccu_div axi_aclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_AXI_ACLK,
+ .cfg0 = 0x138,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("axi-aclk",
+ axi_parents,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static const struct clk_parent_data axi_aclk_pd[] = {
+ { .hw = &axi_aclk.common.hw }
+};
+
+static const struct clk_parent_data perisys_ahb_hclk_parents[] = {
+ { .hw = &gmac_pll_clk.common.hw },
+ { .index = 0 },
+};
+
+static struct ccu_div perisys_ahb_hclk = {
+ .enable = BIT(6),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_PERI_AHB_HCLK,
+ .cfg0 = 0x140,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("perisys-ahb-hclk",
+ perisys_ahb_hclk_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data perisys_ahb_hclk_pd[] = {
+ { .hw = &perisys_ahb_hclk.common.hw }
+};
+
+static const struct clk_hw *perisys_ahb_hclk_parent[] = {
+ &perisys_ahb_hclk.common.hw
+};
+
+static struct ccu_div perisys_apb_pclk = {
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .clkid = CLK_PERI_APB_PCLK,
+ .cfg0 = 0x150,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("perisys-apb-pclk",
+ perisys_ahb_hclk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data perisys_apb_pclk_pd[] = {
+ { .hw = &perisys_apb_pclk.common.hw }
+};
+
+static struct ccu_div peri2sys_apb_pclk = {
+ .div = TH_CCU_DIV_FLAGS(4, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_PERI2APB_PCLK,
+ .cfg0 = 0x150,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("peri2sys-apb-pclk",
+ gmac_pll_clk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data peri2sys_apb_pclk_pd[] = {
+ { .hw = &peri2sys_apb_pclk.common.hw }
+};
+
+static struct clk_fixed_factor osc12m_clk = {
+ .div = 2,
+ .mult = 1,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("osc_12m",
+ osc_24m_clk,
+ &clk_fixed_factor_ops,
+ 0),
+};
+
+static const char * const out_parents[] = { "osc_24m", "osc_12m" };
+
+static struct ccu_div out1_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .clkid = CLK_OUT1,
+ .cfg0 = 0x1b4,
+ .hw.init = CLK_HW_INIT_PARENTS("out1",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out2_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .clkid = CLK_OUT2,
+ .cfg0 = 0x1b8,
+ .hw.init = CLK_HW_INIT_PARENTS("out2",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out3_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .clkid = CLK_OUT3,
+ .cfg0 = 0x1bc,
+ .hw.init = CLK_HW_INIT_PARENTS("out3",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out4_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .clkid = CLK_OUT4,
+ .cfg0 = 0x1c0,
+ .hw.init = CLK_HW_INIT_PARENTS("out4",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data apb_parents[] = {
+ { .hw = &gmac_pll_clk.common.hw },
+ { .index = 0 },
+};
+
+static struct ccu_div apb_pclk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(7, 1),
+ .common = {
+ .clkid = CLK_APB_PCLK,
+ .cfg0 = 0x1c4,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("apb-pclk",
+ apb_parents,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static const struct clk_hw *npu_parents[] = {
+ &gmac_pll_clk.common.hw,
+ &video_pll_clk.common.hw
+};
+
+static struct ccu_div npu_clk = {
+ .enable = BIT(4),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(6, 1),
+ .common = {
+ .clkid = CLK_NPU,
+ .cfg0 = 0x1c8,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("npu",
+ npu_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vi_clk = {
+ .div = TH_CCU_DIV_FLAGS(16, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VI,
+ .cfg0 = 0x1d0,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("vi",
+ video_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static struct ccu_div vi_ahb_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VI_AHB,
+ .cfg0 = 0x1d0,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("vi-ahb",
+ video_pll_clk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vo_axi_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VO_AXI,
+ .cfg0 = 0x1dc,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("vo-axi",
+ video_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static struct ccu_div vp_apb_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VP_APB,
+ .cfg0 = 0x1e0,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("vp-apb",
+ gmac_pll_clk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vp_axi_clk = {
+ .enable = BIT(15),
+ .div = TH_CCU_DIV_FLAGS(8, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VP_AXI,
+ .cfg0 = 0x1e0,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("vp-axi",
+ video_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static struct ccu_div venc_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VENC,
+ .cfg0 = 0x1e4,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("venc",
+ gmac_pll_clk_parent,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div dpu0_clk = {
+ .div_en = BIT(8),
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_DPU0,
+ .cfg0 = 0x1e8,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("dpu0",
+ dpu0_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static const struct clk_parent_data dpu0_clk_pd[] = {
+ { .hw = &dpu0_clk.common.hw }
+};
+
+static struct ccu_div dpu1_clk = {
+ .div_en = BIT(8),
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_DPU1,
+ .cfg0 = 0x1ec,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("dpu1",
+ dpu1_pll_clk_parent,
+ &ccu_div_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static const struct clk_parent_data dpu1_clk_pd[] = {
+ { .hw = &dpu1_clk.common.hw }
+};
+
+static CLK_FIXED_FACTOR_HW(emmc_sdio_ref_clk, "emmc-sdio-ref",
+ &video_pll_clk.common.hw, 4, 1, 0);
+
+static const struct clk_parent_data emmc_sdio_ref_clk_pd[] = {
+ { .hw = &emmc_sdio_ref_clk.hw },
+};
+
+static CCU_GATE(CLK_BROM, brom_clk, "brom", ahb2_cpusys_hclk_pd, 0x100, 4, 0);
+static CCU_GATE(CLK_BMU, bmu_clk, "bmu", axi4_cpusys2_aclk_pd, 0x100, 5, 0);
+static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "aon2cpu-a2x", axi4_cpusys2_aclk_pd,
+ 0x134, 8, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", axi4_cpusys2_aclk_pd,
+ 0x134, 7, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_pd,
+ 0x138, 8, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", axi4_cpusys2_aclk_pd,
+ 0x140, 9, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hclk", perisys_ahb_hclk_pd,
+ 0x150, 9, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hclk", perisys_ahb_hclk_pd,
+ 0x150, 10, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hclk", perisys_ahb_hclk_pd,
+ 0x150, 11, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_PERISYS_APB4_HCLK, perisys_apb4_hclk, "perisys-apb4-hclk", perisys_ahb_hclk_pd,
+ 0x150, 12, 0);
+static const struct clk_parent_data perisys_apb4_hclk_pd[] = {
+ { .hw = &perisys_apb4_hclk.gate.hw },
+};
+
+static CCU_GATE(CLK_NPU_AXI, npu_axi_clk, "npu-axi", axi_aclk_pd, 0x1c8, 5, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_CPU2VP, cpu2vp_clk, "cpu2vp", axi_aclk_pd, 0x1e0, 13, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_EMMC_SDIO, emmc_sdio_clk, "emmc-sdio", emmc_sdio_ref_clk_pd, 0x204, 30, 0);
+static CCU_GATE(CLK_GMAC1, gmac1_clk, "gmac1", gmac_pll_clk_pd, 0x204, 26, 0);
+static CCU_GATE(CLK_PADCTRL1, padctrl1_clk, "padctrl1", perisys_apb_pclk_pd, 0x204, 24, 0);
+static CCU_GATE(CLK_DSMART, dsmart_clk, "dsmart", perisys_apb_pclk_pd, 0x204, 23, 0);
+static CCU_GATE(CLK_PADCTRL0, padctrl0_clk, "padctrl0", perisys_apb4_hclk_pd, 0x204, 22, 0);
+static CCU_GATE(CLK_GMAC_AXI, gmac_axi_clk, "gmac-axi", axi4_cpusys2_aclk_pd, 0x204, 21, 0);
+static CCU_GATE(CLK_GPIO3, gpio3_clk, "gpio3-clk", peri2sys_apb_pclk_pd, 0x204, 20, 0);
+static CCU_GATE(CLK_GMAC0, gmac0_clk, "gmac0", gmac_pll_clk_pd, 0x204, 19, 0);
+static CCU_GATE(CLK_PWM, pwm_clk, "pwm", perisys_apb_pclk_pd, 0x204, 18, 0);
+static CCU_GATE(CLK_QSPI0, qspi0_clk, "qspi0", video_pll_clk_pd, 0x204, 17, 0);
+static CCU_GATE(CLK_QSPI1, qspi1_clk, "qspi1", video_pll_clk_pd, 0x204, 16, 0);
+static CCU_GATE(CLK_SPI, spi_clk, "spi", video_pll_clk_pd, 0x204, 15, 0);
+static CCU_GATE(CLK_UART0_PCLK, uart0_pclk, "uart0-pclk", perisys_apb_pclk_pd, 0x204, 14, 0);
+static CCU_GATE(CLK_UART1_PCLK, uart1_pclk, "uart1-pclk", perisys_apb_pclk_pd, 0x204, 13, 0);
+static CCU_GATE(CLK_UART2_PCLK, uart2_pclk, "uart2-pclk", perisys_apb_pclk_pd, 0x204, 12, 0);
+static CCU_GATE(CLK_UART3_PCLK, uart3_pclk, "uart3-pclk", perisys_apb_pclk_pd, 0x204, 11, 0);
+static CCU_GATE(CLK_UART4_PCLK, uart4_pclk, "uart4-pclk", perisys_apb_pclk_pd, 0x204, 10, 0);
+static CCU_GATE(CLK_UART5_PCLK, uart5_pclk, "uart5-pclk", perisys_apb_pclk_pd, 0x204, 9, 0);
+static CCU_GATE(CLK_GPIO0, gpio0_clk, "gpio0-clk", perisys_apb_pclk_pd, 0x204, 8, 0);
+static CCU_GATE(CLK_GPIO1, gpio1_clk, "gpio1-clk", perisys_apb_pclk_pd, 0x204, 7, 0);
+static CCU_GATE(CLK_GPIO2, gpio2_clk, "gpio2-clk", peri2sys_apb_pclk_pd, 0x204, 6, 0);
+static CCU_GATE(CLK_I2C0, i2c0_clk, "i2c0", perisys_apb_pclk_pd, 0x204, 5, 0);
+static CCU_GATE(CLK_I2C1, i2c1_clk, "i2c1", perisys_apb_pclk_pd, 0x204, 4, 0);
+static CCU_GATE(CLK_I2C2, i2c2_clk, "i2c2", perisys_apb_pclk_pd, 0x204, 3, 0);
+static CCU_GATE(CLK_I2C3, i2c3_clk, "i2c3", perisys_apb_pclk_pd, 0x204, 2, 0);
+static CCU_GATE(CLK_I2C4, i2c4_clk, "i2c4", perisys_apb_pclk_pd, 0x204, 1, 0);
+static CCU_GATE(CLK_I2C5, i2c5_clk, "i2c5", perisys_apb_pclk_pd, 0x204, 0, 0);
+static CCU_GATE(CLK_SPINLOCK, spinlock_clk, "spinlock", ahb2_cpusys_hclk_pd, 0x208, 10, 0);
+static CCU_GATE(CLK_DMA, dma_clk, "dma", axi4_cpusys2_aclk_pd, 0x208, 8, 0);
+static CCU_GATE(CLK_MBOX0, mbox0_clk, "mbox0", apb3_cpusys_pclk_pd, 0x208, 7, 0);
+static CCU_GATE(CLK_MBOX1, mbox1_clk, "mbox1", apb3_cpusys_pclk_pd, 0x208, 6, 0);
+static CCU_GATE(CLK_MBOX2, mbox2_clk, "mbox2", apb3_cpusys_pclk_pd, 0x208, 5, 0);
+static CCU_GATE(CLK_MBOX3, mbox3_clk, "mbox3", apb3_cpusys_pclk_pd, 0x208, 4, 0);
+static CCU_GATE(CLK_WDT0, wdt0_clk, "wdt0", apb3_cpusys_pclk_pd, 0x208, 3, 0);
+static CCU_GATE(CLK_WDT1, wdt1_clk, "wdt1", apb3_cpusys_pclk_pd, 0x208, 2, 0);
+static CCU_GATE(CLK_TIMER0, timer0_clk, "timer0", apb3_cpusys_pclk_pd, 0x208, 1, 0);
+static CCU_GATE(CLK_TIMER1, timer1_clk, "timer1", apb3_cpusys_pclk_pd, 0x208, 0, 0);
+static CCU_GATE(CLK_SRAM0, sram0_clk, "sram0", axi_aclk_pd, 0x20c, 4, 0);
+static CCU_GATE(CLK_SRAM1, sram1_clk, "sram1", axi_aclk_pd, 0x20c, 3, 0);
+static CCU_GATE(CLK_SRAM2, sram2_clk, "sram2", axi_aclk_pd, 0x20c, 2, 0);
+static CCU_GATE(CLK_SRAM3, sram3_clk, "sram3", axi_aclk_pd, 0x20c, 1, 0);
+
+static CCU_GATE(CLK_AXI4_VO_ACLK, axi4_vo_aclk, "axi4-vo-aclk",
+ video_pll_clk_pd, 0x0, 0, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_GPU_CORE, gpu_core_clk, "gpu-core-clk", video_pll_clk_pd,
+ 0x0, 3, 0);
+static CCU_GATE(CLK_GPU_CFG_ACLK, gpu_cfg_aclk, "gpu-cfg-aclk",
+ video_pll_clk_pd, 0x0, 4, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_DPU_PIXELCLK0, dpu0_pixelclk, "dpu0-pixelclk",
+ dpu0_clk_pd, 0x0, 5, CLK_SET_RATE_PARENT);
+static CCU_GATE(CLK_DPU_PIXELCLK1, dpu1_pixelclk, "dpu1-pixelclk",
+ dpu1_clk_pd, 0x0, 6, CLK_SET_RATE_PARENT);
+static CCU_GATE(CLK_DPU_HCLK, dpu_hclk, "dpu-hclk", video_pll_clk_pd, 0x0,
+ 7, 0);
+static CCU_GATE(CLK_DPU_ACLK, dpu_aclk, "dpu-aclk", video_pll_clk_pd, 0x0,
+ 8, 0);
+static CCU_GATE(CLK_DPU_CCLK, dpu_cclk, "dpu-cclk", video_pll_clk_pd, 0x0,
+ 9, 0);
+static CCU_GATE(CLK_HDMI_SFR, hdmi_sfr_clk, "hdmi-sfr-clk", video_pll_clk_pd,
+ 0x0, 10, 0);
+static CCU_GATE(CLK_HDMI_PCLK, hdmi_pclk, "hdmi-pclk", video_pll_clk_pd, 0x0,
+ 11, 0);
+static CCU_GATE(CLK_HDMI_CEC, hdmi_cec_clk, "hdmi-cec-clk", video_pll_clk_pd,
+ 0x0, 12, 0);
+static CCU_GATE(CLK_MIPI_DSI0_PCLK, mipi_dsi0_pclk, "mipi-dsi0-pclk",
+ video_pll_clk_pd, 0x0, 13, 0);
+static CCU_GATE(CLK_MIPI_DSI1_PCLK, mipi_dsi1_pclk, "mipi-dsi1-pclk",
+ video_pll_clk_pd, 0x0, 14, 0);
+static CCU_GATE(CLK_MIPI_DSI0_CFG, mipi_dsi0_cfg_clk, "mipi-dsi0-cfg-clk",
+ video_pll_clk_pd, 0x0, 15, 0);
+static CCU_GATE(CLK_MIPI_DSI1_CFG, mipi_dsi1_cfg_clk, "mipi-dsi1-cfg-clk",
+ video_pll_clk_pd, 0x0, 16, 0);
+static CCU_GATE(CLK_MIPI_DSI0_REFCLK, mipi_dsi0_refclk, "mipi-dsi0-refclk",
+ video_pll_clk_pd, 0x0, 17, 0);
+static CCU_GATE(CLK_MIPI_DSI1_REFCLK, mipi_dsi1_refclk, "mipi-dsi1-refclk",
+ video_pll_clk_pd, 0x0, 18, 0);
+static CCU_GATE(CLK_HDMI_I2S, hdmi_i2s_clk, "hdmi-i2s-clk", video_pll_clk_pd,
+ 0x0, 19, 0);
+static CCU_GATE(CLK_X2H_DPU1_ACLK, x2h_dpu1_aclk, "x2h-dpu1-aclk",
+ video_pll_clk_pd, 0x0, 20, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_X2H_DPU_ACLK, x2h_dpu_aclk, "x2h-dpu-aclk",
+ video_pll_clk_pd, 0x0, 21, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_AXI4_VO_PCLK, axi4_vo_pclk, "axi4-vo-pclk",
+ video_pll_clk_pd, 0x0, 22, 0);
+static CCU_GATE(CLK_IOPMP_VOSYS_DPU_PCLK, iopmp_vosys_dpu_pclk,
+ "iopmp-vosys-dpu-pclk", video_pll_clk_pd, 0x0, 23, 0);
+static CCU_GATE(CLK_IOPMP_VOSYS_DPU1_PCLK, iopmp_vosys_dpu1_pclk,
+ "iopmp-vosys-dpu1-pclk", video_pll_clk_pd, 0x0, 24, 0);
+static CCU_GATE(CLK_IOPMP_VOSYS_GPU_PCLK, iopmp_vosys_gpu_pclk,
+ "iopmp-vosys-gpu-pclk", video_pll_clk_pd, 0x0, 25, 0);
+static CCU_GATE(CLK_IOPMP_DPU1_ACLK, iopmp_dpu1_aclk, "iopmp-dpu1-aclk",
+ video_pll_clk_pd, 0x0, 27, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_IOPMP_DPU_ACLK, iopmp_dpu_aclk, "iopmp-dpu-aclk",
+ video_pll_clk_pd, 0x0, 28, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_IOPMP_GPU_ACLK, iopmp_gpu_aclk, "iopmp-gpu-aclk",
+ video_pll_clk_pd, 0x0, 29, CLK_IS_CRITICAL);
+static CCU_GATE(CLK_MIPIDSI0_PIXCLK, mipi_dsi0_pixclk, "mipi-dsi0-pixclk",
+ video_pll_clk_pd, 0x0, 30, 0);
+static CCU_GATE(CLK_MIPIDSI1_PIXCLK, mipi_dsi1_pixclk, "mipi-dsi1-pixclk",
+ video_pll_clk_pd, 0x0, 31, 0);
+static CCU_GATE(CLK_HDMI_PIXCLK, hdmi_pixclk, "hdmi-pixclk", video_pll_clk_pd,
+ 0x4, 0, 0);
+
+static CLK_FIXED_FACTOR_HW(gmac_pll_clk_100m, "gmac-pll-clk-100m",
+ &gmac_pll_clk.common.hw, 10, 1, 0);
+
+static const struct clk_parent_data uart_sclk_parents[] = {
+ { .hw = &gmac_pll_clk_100m.hw },
+ { .index = 0 },
+};
+
+static struct ccu_mux uart_sclk = {
+ .clkid = CLK_UART_SCLK,
+ .reg = 0x210,
+ .mux = TH_CCU_MUX("uart-sclk", uart_sclk_parents, 0, 1),
+};
+
+static struct ccu_common *th1520_pll_clks[] = {
+ &cpu_pll0_clk.common,
+ &cpu_pll1_clk.common,
+ &gmac_pll_clk.common,
+ &video_pll_clk.common,
+ &dpu0_pll_clk.common,
+ &dpu1_pll_clk.common,
+ &tee_pll_clk.common,
+};
+
+static struct ccu_common *th1520_div_clks[] = {
+ &c910_bus_clk.common,
+ &ahb2_cpusys_hclk.common,
+ &apb3_cpusys_pclk.common,
+ &axi4_cpusys2_aclk.common,
+ &perisys_ahb_hclk.common,
+ &perisys_apb_pclk.common,
+ &axi_aclk.common,
+ &peri2sys_apb_pclk.common,
+ &out1_clk.common,
+ &out2_clk.common,
+ &out3_clk.common,
+ &out4_clk.common,
+ &apb_pclk.common,
+ &npu_clk.common,
+ &vi_clk.common,
+ &vi_ahb_clk.common,
+ &vo_axi_clk.common,
+ &vp_apb_clk.common,
+ &vp_axi_clk.common,
+ &venc_clk.common,
+ &dpu0_clk.common,
+ &dpu1_clk.common,
+};
+
+static struct ccu_mux *th1520_mux_clks[] = {
+ &c910_i0_clk,
+ &c910_clk,
+ &uart_sclk,
+};
+
+static struct ccu_gate *th1520_gate_clks[] = {
+ &emmc_sdio_clk,
+ &aon2cpu_a2x_clk,
+ &x2x_cpusys_clk,
+ &brom_clk,
+ &bmu_clk,
+ &cpu2aon_x2h_clk,
+ &cpu2peri_x2h_clk,
+ &cpu2vp_clk,
+ &perisys_apb1_hclk,
+ &perisys_apb2_hclk,
+ &perisys_apb3_hclk,
+ &perisys_apb4_hclk,
+ &npu_axi_clk,
+ &gmac1_clk,
+ &padctrl1_clk,
+ &dsmart_clk,
+ &padctrl0_clk,
+ &gmac_axi_clk,
+ &gpio3_clk,
+ &gmac0_clk,
+ &pwm_clk,
+ &qspi0_clk,
+ &qspi1_clk,
+ &spi_clk,
+ &uart0_pclk,
+ &uart1_pclk,
+ &uart2_pclk,
+ &uart3_pclk,
+ &uart4_pclk,
+ &uart5_pclk,
+ &gpio0_clk,
+ &gpio1_clk,
+ &gpio2_clk,
+ &i2c0_clk,
+ &i2c1_clk,
+ &i2c2_clk,
+ &i2c3_clk,
+ &i2c4_clk,
+ &i2c5_clk,
+ &spinlock_clk,
+ &dma_clk,
+ &mbox0_clk,
+ &mbox1_clk,
+ &mbox2_clk,
+ &mbox3_clk,
+ &wdt0_clk,
+ &wdt1_clk,
+ &timer0_clk,
+ &timer1_clk,
+ &sram0_clk,
+ &sram1_clk,
+ &sram2_clk,
+ &sram3_clk,
+};
+
+static struct ccu_gate *th1520_vo_gate_clks[] = {
+ &axi4_vo_aclk,
+ &gpu_core_clk,
+ &gpu_cfg_aclk,
+ &dpu0_pixelclk,
+ &dpu1_pixelclk,
+ &dpu_hclk,
+ &dpu_aclk,
+ &dpu_cclk,
+ &hdmi_sfr_clk,
+ &hdmi_pclk,
+ &hdmi_cec_clk,
+ &mipi_dsi0_pclk,
+ &mipi_dsi1_pclk,
+ &mipi_dsi0_cfg_clk,
+ &mipi_dsi1_cfg_clk,
+ &mipi_dsi0_refclk,
+ &mipi_dsi1_refclk,
+ &hdmi_i2s_clk,
+ &x2h_dpu1_aclk,
+ &x2h_dpu_aclk,
+ &axi4_vo_pclk,
+ &iopmp_vosys_dpu_pclk,
+ &iopmp_vosys_dpu1_pclk,
+ &iopmp_vosys_gpu_pclk,
+ &iopmp_dpu1_aclk,
+ &iopmp_dpu_aclk,
+ &iopmp_gpu_aclk,
+ &mipi_dsi0_pixclk,
+ &mipi_dsi1_pixclk,
+ &hdmi_pixclk
+};
+
+static const struct regmap_config th1520_clk_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+struct th1520_plat_data {
+ struct ccu_common **th1520_pll_clks;
+ struct ccu_common **th1520_div_clks;
+ struct ccu_mux **th1520_mux_clks;
+ struct ccu_gate **th1520_gate_clks;
+
+ int nr_clks;
+ int nr_pll_clks;
+ int nr_div_clks;
+ int nr_mux_clks;
+ int nr_gate_clks;
+};
+
+static const struct th1520_plat_data th1520_ap_platdata = {
+ .th1520_pll_clks = th1520_pll_clks,
+ .th1520_div_clks = th1520_div_clks,
+ .th1520_mux_clks = th1520_mux_clks,
+ .th1520_gate_clks = th1520_gate_clks,
+
+ .nr_clks = CLK_C910_BUS + 1,
+
+ .nr_pll_clks = ARRAY_SIZE(th1520_pll_clks),
+ .nr_div_clks = ARRAY_SIZE(th1520_div_clks),
+ .nr_mux_clks = ARRAY_SIZE(th1520_mux_clks),
+ .nr_gate_clks = ARRAY_SIZE(th1520_gate_clks),
+};
+
+static const struct th1520_plat_data th1520_vo_platdata = {
+ .th1520_gate_clks = th1520_vo_gate_clks,
+
+ .nr_clks = CLK_HDMI_PIXCLK + 1,
+
+ .nr_gate_clks = ARRAY_SIZE(th1520_vo_gate_clks),
+};
+
+/*
+ * Maintain clock rate of c910_bus_clk below TH1520_C910_BUS_MAX_RATE (750MHz)
+ * when its parent, c910_clk, changes the rate.
+ *
+ * Additionally, TRM is unclear about c910_bus_clk behavior when the divisor is
+ * set below 2, thus we should ensure the new divisor stays in (2, MAXDIVISOR).
+ */
+static unsigned long c910_bus_clk_divisor(struct ccu_div *cd,
+ unsigned long parent_rate)
+{
+ return clamp(DIV_ROUND_UP(parent_rate, TH1520_C910_BUS_MAX_RATE),
+ 2U, 1U << cd->div.width);
+}
+
+static int c910_clk_notifier_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ unsigned long new_divisor, ref_rate;
+
+ if (action != PRE_RATE_CHANGE && action != POST_RATE_CHANGE)
+ return NOTIFY_DONE;
+
+ new_divisor = c910_bus_clk_divisor(&c910_bus_clk, cnd->new_rate);
+
+ if (cnd->new_rate > cnd->old_rate) {
+ /*
+ * Scaling up. Adjust c910_bus_clk divisor
+ * - before c910_clk rate change to ensure the constraints
+ * aren't broken after scaling to higher rates,
+ * - after c910_clk rate change to keep c910_bus_clk as high as
+ * possible
+ */
+ ref_rate = action == PRE_RATE_CHANGE ?
+ cnd->old_rate : cnd->new_rate;
+ clk_set_rate(c910_bus_clk.common.hw.clk,
+ ref_rate / new_divisor);
+ } else if (cnd->new_rate < cnd->old_rate &&
+ action == POST_RATE_CHANGE) {
+ /*
+ * Scaling down. Adjust c910_bus_clk divisor only after
+ * c910_clk rate change to keep c910_bus_clk as high as
+ * possible, Scaling down never breaks the constraints.
+ */
+ clk_set_rate(c910_bus_clk.common.hw.clk,
+ cnd->new_rate / new_divisor);
+ } else {
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block c910_clk_notifier = {
+ .notifier_call = c910_clk_notifier_cb,
+};
+
+static int th1520_clk_probe(struct platform_device *pdev)
+{
+ const struct th1520_plat_data *plat_data;
+ struct device *dev = &pdev->dev;
+ struct clk_hw_onecell_data *priv;
+ struct clk *notifier_clk;
+
+ struct regmap *map;
+ void __iomem *base;
+ int ret, i;
+
+ plat_data = device_get_match_data(&pdev->dev);
+ if (!plat_data)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "No device match data found\n");
+
+ priv = devm_kzalloc(dev, struct_size(priv, hws, plat_data->nr_clks), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->num = plat_data->nr_clks;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ map = devm_regmap_init_mmio(dev, base, &th1520_clk_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ for (i = 0; i < plat_data->nr_pll_clks; i++) {
+ struct ccu_pll *cp = hw_to_ccu_pll(&plat_data->th1520_pll_clks[i]->hw);
+
+ plat_data->th1520_pll_clks[i]->map = map;
+
+ ret = devm_clk_hw_register(dev, &plat_data->th1520_pll_clks[i]->hw);
+ if (ret)
+ return ret;
+
+ priv->hws[cp->common.clkid] = &cp->common.hw;
+ }
+
+ for (i = 0; i < plat_data->nr_div_clks; i++) {
+ struct ccu_div *cd = hw_to_ccu_div(&plat_data->th1520_div_clks[i]->hw);
+
+ plat_data->th1520_div_clks[i]->map = map;
+
+ ret = devm_clk_hw_register(dev, &plat_data->th1520_div_clks[i]->hw);
+ if (ret)
+ return ret;
+
+ priv->hws[cd->common.clkid] = &cd->common.hw;
+ }
+
+ for (i = 0; i < plat_data->nr_mux_clks; i++) {
+ struct ccu_mux *cm = plat_data->th1520_mux_clks[i];
+
+ cm->mux.reg = base + cm->reg;
+
+ ret = devm_clk_hw_register(dev, &cm->mux.hw);
+ if (ret)
+ return ret;
+
+ priv->hws[cm->clkid] = &cm->mux.hw;
+ }
+
+ for (i = 0; i < plat_data->nr_gate_clks; i++) {
+ struct ccu_gate *cg = plat_data->th1520_gate_clks[i];
+
+ cg->gate.reg = base + cg->reg;
+
+ ret = devm_clk_hw_register(dev, &cg->gate.hw);
+ if (ret)
+ return ret;
+
+ priv->hws[cg->clkid] = &cg->gate.hw;
+ }
+
+ if (plat_data == &th1520_ap_platdata) {
+ ret = devm_clk_hw_register(dev, &osc12m_clk.hw);
+ if (ret)
+ return ret;
+ priv->hws[CLK_OSC12M] = &osc12m_clk.hw;
+
+ ret = devm_clk_hw_register(dev, &gmac_pll_clk_100m.hw);
+ if (ret)
+ return ret;
+ priv->hws[CLK_PLL_GMAC_100M] = &gmac_pll_clk_100m.hw;
+
+ ret = devm_clk_hw_register(dev, &emmc_sdio_ref_clk.hw);
+ if (ret)
+ return ret;
+
+ notifier_clk = devm_clk_hw_get_clk(dev, &c910_clk.mux.hw,
+ "dvfs");
+ ret = devm_clk_notifier_register(dev, notifier_clk,
+ &c910_clk_notifier);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id th1520_clk_match[] = {
+ {
+ .compatible = "thead,th1520-clk-ap",
+ .data = &th1520_ap_platdata,
+ },
+ {
+ .compatible = "thead,th1520-clk-vo",
+ .data = &th1520_vo_platdata,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, th1520_clk_match);
+
+static struct platform_driver th1520_clk_driver = {
+ .probe = th1520_clk_probe,
+ .driver = {
+ .name = "th1520-clk",
+ .of_match_table = th1520_clk_match,
+ },
+};
+module_platform_driver(th1520_clk_driver);
+
+MODULE_DESCRIPTION("T-HEAD TH1520 AP Clock driver");
+MODULE_AUTHOR("Yangtao Li <frank.li@vivo.com>");
+MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
+MODULE_LICENSE("GPL");