clk: tegra: Convert CCLKG mux to mux + clock divider on Tegra30
authorDmitry Osipenko <digetx@gmail.com>
Sun, 12 Aug 2018 18:42:28 +0000 (21:42 +0300)
committerDmitry Osipenko <digetx@gmail.com>
Sat, 9 Feb 2019 19:15:36 +0000 (22:15 +0300)
Some of the CCLKG parents aren't accessible via device tree because they
are created as non-DT clocks. Apparently there is no reason to define
these clocks in that manner, hence convert CCLKG mux to mux + clock
divider to remove the non-DT parent clocks. Now it is possible to request
all of CCLKG parents from device tree, which is necessary for the CPUFreq
driver.

Note that CCLKG bypasses clock divider only if PLLX is selected as the
parent, hence previous CCLKG parents definition was incorrect.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
drivers/clk/tegra/clk-super.c
drivers/clk/tegra/clk-tegra210.c
drivers/clk/tegra/clk-tegra30.c
drivers/clk/tegra/clk.h

index 84267cf..8ba58f7 100644 (file)
@@ -65,6 +65,8 @@ static u8 clk_super_get_parent(struct clk_hw *hw)
            (source == mux->pllx_index))
                source = mux->div2_index;
 
+       mux->pllx_parent = (source == mux->pllx_index);
+
        return source;
 }
 
@@ -114,6 +116,8 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
        writel_relaxed(val, mux->reg);
        udelay(2);
 
+       mux->pllx_parent = (index == mux->pllx_index);
+
 out:
        if (mux->lock)
                spin_unlock_irqrestore(mux->lock, flags);
@@ -132,6 +136,9 @@ static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
        struct clk_hw *div_hw = &super->frac_div.hw;
 
+       if ((super->flags & TEGRA_CCLKG_DIVIDER) && super->pllx_parent)
+               return *parent_rate;
+
        __clk_hw_set_clk(div_hw, hw);
 
        return super->div_ops->round_rate(div_hw, rate, parent_rate);
@@ -143,6 +150,9 @@ static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
        struct clk_hw *div_hw = &super->frac_div.hw;
 
+       if ((super->flags & TEGRA_CCLKG_DIVIDER) && super->pllx_parent)
+               return parent_rate;
+
        __clk_hw_set_clk(div_hw, hw);
 
        return super->div_ops->recalc_rate(div_hw, parent_rate);
@@ -154,6 +164,9 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
        struct clk_hw *div_hw = &super->frac_div.hw;
 
+       if ((super->flags & TEGRA_CCLKG_DIVIDER) && super->pllx_parent)
+               return 0;
+
        __clk_hw_set_clk(div_hw, hw);
 
        return super->div_ops->set_rate(div_hw, rate, parent_rate);
@@ -204,7 +217,7 @@ struct clk *tegra_clk_register_super_mux(const char *name,
 }
 
 struct clk *tegra_clk_register_super_clk(const char *name,
-               const char * const *parent_names, u8 num_parents,
+               const char * const *parent_names, u8 num_parents, u8 pllx_index,
                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
                spinlock_t *lock)
 {
@@ -232,6 +245,7 @@ struct clk *tegra_clk_register_super_clk(const char *name,
        super->frac_div.frac_width = 1;
        super->frac_div.lock = lock;
        super->div_ops = &tegra_clk_frac_div_ops;
+       super->pllx_index = pllx_index;
 
        /* Data in .init is copied by clk_register(), so stack variable OK */
        super->hw.init = &init;
index 7545af7..b183dde 100644 (file)
@@ -3027,7 +3027,7 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
        clks[TEGRA210_CLK_CML1] = clk;
 
        clk = tegra_clk_register_super_clk("aclk", aclk_parents,
-                               ARRAY_SIZE(aclk_parents), 0, clk_base + 0x6e0,
+                               ARRAY_SIZE(aclk_parents), 0, 0, clk_base + 0x6e0,
                                0, NULL);
        clks[TEGRA210_CLK_ACLK] = clk;
 
index fa8d573..2a45d70 100644 (file)
@@ -902,8 +902,8 @@ static void __init tegra30_pll_init(void)
 }
 
 static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
-                                       "pll_p_cclkg", "pll_p_out4_cclkg",
-                                       "pll_p_out3_cclkg", "unused", "pll_x" };
+                                       "pll_p", "pll_p_out4", "pll_p_out3",
+                                       "unused", "pll_x" };
 static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
                                         "pll_p_cclklp", "pll_p_out4_cclklp",
                                         "pll_p_out3_cclklp", "unused", "pll_x",
@@ -916,39 +916,11 @@ static void __init tegra30_super_clk_init(void)
 {
        struct clk *clk;
 
-       /*
-        * Clock input to cclk_g divided from pll_p using
-        * U71 divider of cclk_g.
-        */
-       clk = tegra_clk_register_divider("pll_p_cclkg", "pll_p",
-                               clk_base + SUPER_CCLKG_DIVIDER, 0,
-                               TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
-       clk_register_clkdev(clk, "pll_p_cclkg", NULL);
-
-       /*
-        * Clock input to cclk_g divided from pll_p_out3 using
-        * U71 divider of cclk_g.
-        */
-       clk = tegra_clk_register_divider("pll_p_out3_cclkg", "pll_p_out3",
-                               clk_base + SUPER_CCLKG_DIVIDER, 0,
-                               TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
-       clk_register_clkdev(clk, "pll_p_out3_cclkg", NULL);
-
-       /*
-        * Clock input to cclk_g divided from pll_p_out4 using
-        * U71 divider of cclk_g.
-        */
-       clk = tegra_clk_register_divider("pll_p_out4_cclkg", "pll_p_out4",
-                               clk_base + SUPER_CCLKG_DIVIDER, 0,
-                               TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
-       clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
-
        /* CCLKG */
-       clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents,
-                                 ARRAY_SIZE(cclk_g_parents),
-                                 CLK_SET_RATE_PARENT,
+       clk = tegra_clk_register_super_clk("cclk_g", cclk_g_parents,
+                                 ARRAY_SIZE(cclk_g_parents), 8, 0,
                                  clk_base + CCLKG_BURST_POLICY,
-                                 0, 4, 0, 0, NULL);
+                                 TEGRA_CCLKG_DIVIDER, NULL);
        clks[TEGRA30_CLK_CCLK_G] = clk;
 
        /*
index 09bccbb..34122de 100644 (file)
@@ -680,6 +680,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
  * Flags:
  * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
  *     that this is LP cluster clock.
+ *
+ * TEGRA_CCLKG_DIVIDER - G cluster clock may bypass clocks divider. This flag
+ *     indicates that this is G cluster clock.
  */
 struct tegra_clk_super_mux {
        struct clk_hw   hw;
@@ -691,11 +694,13 @@ struct tegra_clk_super_mux {
        u8              div2_index;
        u8              pllx_index;
        spinlock_t      *lock;
+       bool            pllx_parent;
 };
 
 #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
 
-#define TEGRA_DIVIDER_2 BIT(0)
+#define TEGRA_DIVIDER_2                BIT(0)
+#define TEGRA_CCLKG_DIVIDER    BIT(1)
 
 extern const struct clk_ops tegra_clk_super_ops;
 struct clk *tegra_clk_register_super_mux(const char *name,
@@ -703,7 +708,7 @@ struct clk *tegra_clk_register_super_mux(const char *name,
                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
                u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock);
 struct clk *tegra_clk_register_super_clk(const char *name,
-               const char * const *parent_names, u8 num_parents,
+               const char * const *parent_names, u8 num_parents, u8 pllx_index,
                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
                spinlock_t *lock);