mirror of https://github.com/OpenIPC/firmware.git
185 lines
5.0 KiB
Diff
185 lines
5.0 KiB
Diff
diff -drupN a/drivers/clk/ingenic/clk-pll.c b/drivers/clk/ingenic/clk-pll.c
|
|
--- a/drivers/clk/ingenic/clk-pll.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/clk/ingenic/clk-pll.c 2022-06-09 05:02:28.000000000 +0300
|
|
@@ -0,0 +1,180 @@
|
|
+#include <linux/errno.h>
|
|
+#include <linux/hrtimer.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/clkdev.h>
|
|
+#include "clk.h"
|
|
+#include "clk-pll.h"
|
|
+
|
|
+#define PLL_TIMEOUT_MS 10
|
|
+
|
|
+struct ingenic_clk_pll {
|
|
+ struct clk_hw hw;
|
|
+ void __iomem *lock_reg;
|
|
+ void __iomem *con_reg;
|
|
+ unsigned int rate_count;
|
|
+
|
|
+ struct ingenic_pll_hwdesc *hwdesc;
|
|
+ struct ingenic_pll_rate_table *rate_table;
|
|
+
|
|
+};
|
|
+
|
|
+#define to_clk_pll(_hw) container_of(_hw, struct ingenic_clk_pll, hw)
|
|
+
|
|
+static long ingenic_pll_round_rate(struct clk_hw *hw, unsigned long drate, unsigned long *prate)
|
|
+{
|
|
+ struct ingenic_clk_pll *pll = to_clk_pll(hw);
|
|
+ const struct ingenic_pll_rate_table *rate_table = pll->rate_table;
|
|
+ int i;
|
|
+ unsigned int rate;
|
|
+
|
|
+ /* if pll can't find rate in rate_table, pll can set to big than rate */
|
|
+ for (i = 0; i < pll->rate_count; i++) {
|
|
+ if (drate == rate_table[i].rate)
|
|
+ return rate_table[i].rate;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < pll->rate_count; i++) {
|
|
+ if (drate >= rate_table[i].rate)
|
|
+ return rate_table[i].rate;
|
|
+ }
|
|
+
|
|
+ return rate_table[i - 1].rate;
|
|
+
|
|
+}
|
|
+
|
|
+static unsigned long ingenic_t40_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
+{
|
|
+ struct ingenic_clk_pll *pll = to_clk_pll(hw);
|
|
+ unsigned od0, m, n, od1;
|
|
+ uint32_t val;
|
|
+ uint32_t rate;
|
|
+
|
|
+ val = readl(pll->con_reg);
|
|
+
|
|
+ od0 = (val >> pll->hwdesc->od0_sft) & GENMASK(pll->hwdesc->od0_width - 1, 0);
|
|
+ od1 = (val >> pll->hwdesc->od1_sft) & GENMASK(pll->hwdesc->od1_width - 1, 0);
|
|
+ n = (val >> pll->hwdesc->n_sft) & GENMASK(pll->hwdesc->n_width - 1, 0);
|
|
+ m = (val >> pll->hwdesc->m_sft) & GENMASK(pll->hwdesc->m_width - 1, 0);
|
|
+
|
|
+ rate = (parent_rate / 1000000 ) * m / n / od0 / od1;
|
|
+
|
|
+ //printk("od0: %d, od1: %d, m: %d, n: %d, tmp: %x, tmp2: %x, rate:%ld\n", od0, od1, m, n, tmp, tmp2, rate);
|
|
+ rate = rate*1000000;
|
|
+
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+static int wait_pll_stable(void __iomem *reg, u32 shift)
|
|
+{
|
|
+ unsigned int timeout = 0xffff;
|
|
+
|
|
+ while ((!((readl(reg) >> shift) & 1)) && timeout--);
|
|
+ if (!timeout) {
|
|
+ printk("WARNING : why cannot wait pll stable ???\n");
|
|
+ return -EIO;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ingenic_t40_pll_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate)
|
|
+{
|
|
+ struct ingenic_clk_pll *pll = to_clk_pll(hw);
|
|
+
|
|
+ unsigned int m, n, od1, od0;
|
|
+ unsigned int val, i;
|
|
+
|
|
+ for (i = 0; i < pll->rate_count; i++) {
|
|
+ if (drate == pll->rate_table[i].rate) {
|
|
+ m = pll->rate_table[i].m;
|
|
+ n = pll->rate_table[i].n;
|
|
+ od1 = pll->rate_table[i].od1;
|
|
+ od0 = pll->rate_table[i].od0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ writel(0, pll->con_reg);
|
|
+ val = (m << pll->hwdesc->m_sft) | (n << pll->hwdesc->n_sft) | (od1 << pll->hwdesc->od1_sft) | (od0 << pll->hwdesc->od0_sft) | 1;
|
|
+ writel(val, pll->con_reg);
|
|
+
|
|
+ wait_pll_stable(pll->con_reg, pll->hwdesc->on_bit);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct clk_ops ingenic_t40_pll_clk_ops = {
|
|
+ .recalc_rate = ingenic_t40_pll_recalc_rate,
|
|
+ .round_rate = ingenic_pll_round_rate,
|
|
+ .set_rate = ingenic_t40_pll_set_rate,
|
|
+};
|
|
+
|
|
+static void __init _ingenic_clk_register_pll(struct ingenic_clk_provider *ctx,
|
|
+ const struct ingenic_pll_clock *pll_clk,
|
|
+ void __iomem *base)
|
|
+{
|
|
+ struct ingenic_clk_pll *pll;
|
|
+ struct clk *clk;
|
|
+ struct clk_init_data init;
|
|
+ int ret, len;
|
|
+
|
|
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
|
+ if (!pll) {
|
|
+ pr_err("%s: could not allocate pll clk %s\n",
|
|
+ __func__, pll_clk->dev_name);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ init.name = pll_clk->dev_name;
|
|
+// init.flags = pll_clk->flags;
|
|
+ init.parent_names = &pll_clk->parent_name;
|
|
+ init.num_parents = 1;
|
|
+ init.ops = &ingenic_t40_pll_clk_ops;
|
|
+
|
|
+
|
|
+ pll->hw.init = &init;
|
|
+ pll->hwdesc = pll_clk->hwdesc;
|
|
+ pll->con_reg = base + pll_clk->hwdesc->regoff;
|
|
+
|
|
+ if (pll_clk->rate_table) {
|
|
+ /* find count of rates in rate_table */
|
|
+ for (len = 0; pll_clk->rate_table[len].rate != 0; )
|
|
+ len++;
|
|
+
|
|
+ pll->rate_count = len - 1;
|
|
+ pll->rate_table = kmemdup(pll_clk->rate_table,
|
|
+ pll->rate_count *
|
|
+ sizeof(struct ingenic_pll_rate_table),
|
|
+ GFP_KERNEL);
|
|
+ WARN(!pll->rate_table,
|
|
+ "%s: could not allocate rate table for %s\n",
|
|
+ __func__, pll_clk->name);
|
|
+ }
|
|
+
|
|
+ clk = clk_register(NULL, &pll->hw);
|
|
+ if (IS_ERR(clk)) {
|
|
+ pr_err("%s: failed to register pll clock %s : %ld\n",
|
|
+ __func__, pll_clk->dev_name, PTR_ERR(clk));
|
|
+ kfree(pll);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ __clk_set_flags(clk, 1);
|
|
+ ingenic_clk_add_lookup(ctx, clk, pll_clk->id);
|
|
+
|
|
+
|
|
+ ret = clk_register_clkdev(clk, pll_clk->dev_name, NULL);
|
|
+ if (ret)
|
|
+ pr_err("%s: failed to register lookup for %s : %d",
|
|
+ __func__, pll_clk->dev_name, ret);
|
|
+}
|
|
+
|
|
+void __init ingenic_clk_register_pll(struct ingenic_clk_provider *ctx,
|
|
+ const struct ingenic_pll_clock *pll_list,
|
|
+ unsigned int nr_pll, void __iomem *base)
|
|
+{
|
|
+ int cnt;
|
|
+
|
|
+ for (cnt = 0; cnt < nr_pll; cnt++)
|
|
+ _ingenic_clk_register_pll(ctx, &pll_list[cnt], base);
|
|
+}
|