[Feature] add GA346 baseline version
Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/clk/mediatek/clk-fixup-div.c b/src/kernel/linux/v4.19/drivers/clk/mediatek/clk-fixup-div.c
new file mode 100644
index 0000000..936d2fa
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/clk/mediatek/clk-fixup-div.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk-mtk.h"
+#include "clk-fixup-div.h"
+
+#define div_mask(d) ((1 << (d)) - 1)
+
+static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+ unsigned int val;
+
+ val = readl(fixup_div->reg_fixup) >> fixup_div->divider.shift;
+ val &= div_mask(fixup_div->divider.width);
+ pr_debug("%s: val = %x\n", __func__, val);
+
+ return divider_recalc_rate(hw, parent_rate, val,
+ fixup_div->clk_div_table,
+ fixup_div->divider.flags,
+ fixup_div->divider.width);
+}
+
+static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+
+ pr_debug("%s: rate = %lu, prate = %lu\n", __func__, rate, *prate);
+
+ return divider_round_rate(hw, rate, prate, fixup_div->clk_div_table,
+ fixup_div->divider.width,
+ fixup_div->divider.flags);
+}
+
+static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+ struct clk_divider *div = to_clk_divider(hw);
+ unsigned long flags = 0;
+ int val, value;
+
+ value = divider_get_val(rate, parent_rate, div->table,
+ div->width, div->flags);
+ if (value < 0)
+ return value;
+
+ spin_lock_irqsave(div->lock, flags);
+
+ val = readl(fixup_div->reg_fixup);
+ val &= ~(div_mask(div->width) << div->shift);
+ val |= (u32)value << div->shift;
+
+ writel(val, div->reg);
+ writel(val, fixup_div->reg_fixup);
+
+ pr_debug("%s: %s: reg_fixup = %x\n",
+ __func__, clk_hw_get_name(hw), readl(fixup_div->reg_fixup));
+
+ spin_unlock_irqrestore(div->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops clk_fixup_div_ops = {
+ .recalc_rate = clk_fixup_div_recalc_rate,
+ .round_rate = clk_fixup_div_round_rate,
+ .set_rate = clk_fixup_div_set_rate,
+};
+
+struct clk *mtk_clk_fixup_divider(const char *name, const char *parent,
+ unsigned long flags, void __iomem *reg,
+ void __iomem *reg_fixup, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ struct clk_fixup_div *fixup_div;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL);
+ if (!fixup_div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_fixup_div_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = parent ? &parent : NULL;
+ init.num_parents = parent ? 1 : 0;
+
+ fixup_div->reg_fixup = reg_fixup;
+ fixup_div->divider.reg = reg;
+ fixup_div->divider.flags = clk_divider_flags;
+ fixup_div->divider.shift = shift;
+ fixup_div->divider.width = width;
+ fixup_div->divider.lock = lock;
+ fixup_div->divider.hw.init = &init;
+ fixup_div->ops = &clk_divider_ops;
+
+ clk = clk_register(NULL, &fixup_div->divider.hw);
+ if (IS_ERR(clk))
+ kfree(fixup_div);
+
+ return clk;
+}