ARM: tegra: clock: Verify initial cbus pll descendants
Alex Frid [Sun, 17 Mar 2013 06:45:19 +0000 (23:45 -0700)]
During clock initialization verified that children of cbus plls are
either disabled or known cbus clients (the latter will be backed up
on different pll while cbus pll is re-locked).

Change-Id: Ic03caf204e6d96b2ece0dbb8d80c44836c42590b
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/210236
GVS: Gerrit_Virtual_Submit
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Reviewed-by: Kaz Fukuoka <kfukuoka@nvidia.com>
Reviewed-by: Prashant Malani <pmalani@nvidia.com>

arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/clock.h
arch/arm/mach-tegra/common.c

index 4c7e0d4..ce19610 100644 (file)
@@ -719,6 +719,81 @@ static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
        return 0;
 }
 
+/*
+ * If table refer pll directly it can be scaled only if all its children are OFF
+ */
+static bool tegra_can_scale_pll_direct(struct clk *pll)
+{
+       bool can_scale = true;
+       struct clk *c;
+
+       mutex_lock(&clock_list_lock);
+
+       list_for_each_entry(c, &clocks, node) {
+               if ((clk_get_parent(c) == pll) && (c->state == ON)) {
+                       WARN(1, "tegra: failed initialize %s: in use by %s\n",
+                            pll->name, c->name);
+                       can_scale = false;
+                       break;
+               }
+       }
+       mutex_unlock(&clock_list_lock);
+       return can_scale;
+}
+
+/*
+ * If table entry refer pll as cbus parent it can be scaled as long as all its
+ * children are cbus users (that will be switched to cbus backup during scaling)
+ */
+static bool tegra_can_scale_pll_cbus(struct clk *pll)
+{
+       bool can_scale = true;
+       struct clk *c;
+
+       mutex_lock(&clock_list_lock);
+
+       list_for_each_entry(c, &clocks, node) {
+               if ((clk_get_parent(c) == pll) &&
+                   !(c->flags & PERIPH_ON_CBUS)) {
+                       WARN(1, "tegra: failed initialize %s: in use by %s\n",
+                            pll->name, c->name);
+                       can_scale = false;
+                       break;
+               }
+       }
+       mutex_unlock(&clock_list_lock);
+       return can_scale;
+}
+
+static int tegra_clk_init_cbus_pll_one(struct tegra_clk_init_table *table)
+{
+       bool can_scale = true;
+       struct clk *pll;
+       struct clk *c = tegra_get_clock_by_name(table->name);
+       if (!c)
+               return tegra_clk_init_one_from_table(table);
+
+       if (c->flags & PERIPH_ON_CBUS) {
+               /* table entry refer pllc/c2/c3 indirectly as cbus parent */
+               pll = clk_get_parent(c);
+               can_scale = tegra_can_scale_pll_cbus(pll);
+       } else if (c->state == ON) {
+               /* table entry refer pllc/c2/c3 directly, and it is ON */
+               pll = c;
+               can_scale = tegra_can_scale_pll_direct(pll);
+       }
+
+       if (can_scale)
+               return tegra_clk_init_one_from_table(table);
+       return -EBUSY;
+}
+
+void tegra_clk_init_cbus_plls_from_table(struct tegra_clk_init_table *table)
+{
+       for (; table->name; table++)
+               tegra_clk_init_cbus_pll_one(table);
+}
+
 void tegra_clk_init_from_table(struct tegra_clk_init_table *table)
 {
        for (; table->name; table++)
index bfaa95c..6644ce9 100644 (file)
@@ -272,6 +272,7 @@ void clk_init(struct clk *clk);
 struct clk *tegra_get_clock_by_name(const char *name);
 unsigned long tegra_clk_measure_input_freq(void);
 int clk_reparent(struct clk *c, struct clk *parent);
+void tegra_clk_init_cbus_plls_from_table(struct tegra_clk_init_table *table);
 void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
 void clk_set_cansleep(struct clk *c);
 unsigned long clk_get_max_rate(struct clk *c);
index 0538880..1ad4e24 100644 (file)
@@ -324,9 +324,12 @@ static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
        { "sbc4.sclk",  NULL,           40000000,       false},
        { "sbc5.sclk",  NULL,           40000000,       false},
        { "sbc6.sclk",  NULL,           40000000,       false},
+       { "mselect",    "pll_p",        102000000,      true },
+       { NULL,         NULL,           0,              0},
+};
+static __initdata struct tegra_clk_init_table tegra30_cbus_init_table[] = {
        { "cbus",       "pll_c",        416000000,      false },
        { "pll_c_out1", "pll_c",        208000000,      false },
-       { "mselect",    "pll_p",        102000000,      true },
        { NULL,         NULL,           0,              0},
 };
 #endif
@@ -386,6 +389,16 @@ static __initdata struct tegra_clk_init_table tegra11x_clk_init_table[] = {
        { "sbc4.sclk",  NULL,           40000000,       false},
        { "sbc5.sclk",  NULL,           40000000,       false},
        { "sbc6.sclk",  NULL,           40000000,       false},
+#ifdef CONFIG_TEGRA_PLLM_SCALED
+       { "vi",         "pll_p",        0,              false},
+#endif
+#ifdef CONFIG_TEGRA_SOCTHERM
+       { "soc_therm",  "pll_p",        51000000,       false },
+       { "tsensor",    "clk_m",        500000,         false },
+#endif
+       { NULL,         NULL,           0,              0},
+};
+static __initdata struct tegra_clk_init_table tegra11x_cbus_init_table[] = {
 #ifdef CONFIG_TEGRA_DUAL_CBUS
        { "c2bus",      "pll_c2",       250000000,      false },
        { "c3bus",      "pll_c3",       250000000,      false },
@@ -394,13 +407,6 @@ static __initdata struct tegra_clk_init_table tegra11x_clk_init_table[] = {
        { "cbus",       "pll_c",        250000000,      false },
 #endif
        { "pll_c_out1", "pll_c",        150000000,      false },
-#ifdef CONFIG_TEGRA_PLLM_SCALED
-       { "vi",         "pll_p",        0,              false},
-#endif
-#ifdef CONFIG_TEGRA_SOCTHERM
-       { "soc_therm",  "pll_p",        51000000,       false },
-       { "tsensor",    "clk_m",        500000,         false },
-#endif
        { NULL,         NULL,           0,              0},
 };
 #endif
@@ -731,6 +737,7 @@ void __init tegra30_init_early(void)
        tegra3_init_dvfs();
        tegra_common_init_clock();
        tegra_clk_init_from_table(tegra30_clk_init_table);
+       tegra_clk_init_cbus_plls_from_table(tegra30_cbus_init_table);
        tegra_init_cache(true);
        tegra_pmc_init();
        tegra_powergate_init();
@@ -759,6 +766,7 @@ void __init tegra11x_init_early(void)
        tegra11x_init_dvfs();
        tegra_common_init_clock();
        tegra_clk_init_from_table(tegra11x_clk_init_table);
+       tegra_clk_init_cbus_plls_from_table(tegra11x_cbus_init_table);
        tegra11x_clk_init_la();
        tegra_pmc_init();
        tegra_powergate_init();