Kernel should never gate the EMC clock as it causes immediate lockup, so
removing clk-gate functionality doesn't affect anything. Turning EMC clk
gate into divider allows to implement glitch-less EMC scaling, avoiding
reparenting to a backup clock.

Signed-off-by: Dmitry Osipenko <dig...@gmail.com>
Acked-by: Peter De Schrijver <pdeschrij...@nvidia.com>
---
 drivers/clk/tegra/clk-tegra20.c | 36 ++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index cc857d4d4a86..2bd35418716a 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -578,7 +578,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] 
__initdata = {
        [tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true },
        [tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true },
        [tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },
-       [tegra_clk_emc] = { .dt_id = TEGRA20_CLK_EMC, .present = true },
 };
 
 static unsigned long tegra20_clk_measure_input_freq(void)
@@ -799,6 +798,31 @@ static struct tegra_periph_init_data 
tegra_periph_nodiv_clk_list[] = {
        TEGRA_INIT_DATA_NODIV("disp2",  mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 
2, 26,  0, TEGRA20_CLK_DISP2),
 };
 
+static void __init tegra20_emc_clk_init(void)
+{
+       struct clk *clk;
+
+       clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+                              ARRAY_SIZE(mux_pllmcp_clkm),
+                              CLK_SET_RATE_NO_REPARENT,
+                              clk_base + CLK_SOURCE_EMC,
+                              30, 2, 0, &emc_lock);
+
+       clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+                                   &emc_lock);
+       clks[TEGRA20_CLK_MC] = clk;
+
+       /*
+        * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
+        * the same time due to a HW bug, this won't happen because we're
+        * defining 'emc_mux' and 'emc' as a distinct clocks.
+        */
+       clk = clk_register_divider(NULL, "emc", "emc_mux", CLK_IS_CRITICAL,
+                                  clk_base + CLK_SOURCE_EMC, 0, 7,
+                                  0, &emc_lock);
+       clks[TEGRA20_CLK_EMC] = clk;
+}
+
 static void __init tegra20_periph_clk_init(void)
 {
        struct tegra_periph_init_data *data;
@@ -812,15 +836,7 @@ static void __init tegra20_periph_clk_init(void)
        clks[TEGRA20_CLK_AC97] = clk;
 
        /* emc */
-       clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
-                              ARRAY_SIZE(mux_pllmcp_clkm),
-                              CLK_SET_RATE_NO_REPARENT,
-                              clk_base + CLK_SOURCE_EMC,
-                              30, 2, 0, &emc_lock);
-
-       clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
-                                   &emc_lock);
-       clks[TEGRA20_CLK_MC] = clk;
+       tegra20_emc_clk_init();
 
        /* dsi */
        clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
-- 
2.17.1

Reply via email to