clk: samsung: Fix Exynos 5420 pinctrl setup and clock disable failure due to domain...
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>
Fri, 5 Dec 2014 14:15:34 +0000 (15:15 +0100)
committerMichael Turquette <mturquette@linaro.org>
Wed, 17 Dec 2014 15:31:51 +0000 (07:31 -0800)
Audio subsystem clocks are located in separate block. On Exynos 5420 if
clock for this block (from main clock domain) 'mau_epll' is gated then
any read or write to audss registers will block.

This kind of boot hang was observed on Arndale Octa and Peach Pi/Pit
after introducing runtime PM to pl330 DMA driver. After that commit the
'mau_epll' was gated, because the "amba" clock was disabled and there
were no more users of mau_epll.

The system hang on one of steps:
1. Disabling unused clocks from audss block.
2. During audss GPIO setup (just before probing i2s0 because
   samsung_pinmux_setup() tried to access memory from audss block which was
   gated.

Add a workaround for this by enabling the 'mau_epll' clock in probe.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Acked-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Tested-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Tested-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Michael Turquette <mturquette@linaro.org>
drivers/clk/samsung/clk-exynos-audss.c

index b50469f..f7eff82 100644 (file)
@@ -29,6 +29,13 @@ static DEFINE_SPINLOCK(lock);
 static struct clk **clk_table;
 static void __iomem *reg_base;
 static struct clk_onecell_data clk_data;
+/*
+ * On Exynos5420 this will be a clock which has to be enabled before any
+ * access to audss registers. Typically a child of EPLL.
+ *
+ * On other platforms this will be -ENODEV.
+ */
+static struct clk *epll;
 
 #define ASS_CLK_SRC 0x0
 #define ASS_CLK_DIV 0x4
@@ -98,6 +105,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to map audss registers\n");
                return PTR_ERR(reg_base);
        }
+       /* EPLL don't have to be enabled for boards other than Exynos5420 */
+       epll = ERR_PTR(-ENODEV);
 
        clk_table = devm_kzalloc(&pdev->dev,
                                sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
@@ -115,8 +124,20 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
        pll_in = devm_clk_get(&pdev->dev, "pll_in");
        if (!IS_ERR(pll_ref))
                mout_audss_p[0] = __clk_get_name(pll_ref);
-       if (!IS_ERR(pll_in))
+       if (!IS_ERR(pll_in)) {
                mout_audss_p[1] = __clk_get_name(pll_in);
+
+               if (variant == TYPE_EXYNOS5420) {
+                       epll = pll_in;
+
+                       ret = clk_prepare_enable(epll);
+                       if (ret) {
+                               dev_err(&pdev->dev,
+                                               "failed to prepare the epll clock\n");
+                               return ret;
+                       }
+               }
+       }
        clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
                                mout_audss_p, ARRAY_SIZE(mout_audss_p),
                                CLK_SET_RATE_NO_REPARENT,
@@ -203,6 +224,9 @@ unregister:
                        clk_unregister(clk_table[i]);
        }
 
+       if (!IS_ERR(epll))
+               clk_disable_unprepare(epll);
+
        return ret;
 }
 
@@ -221,6 +245,9 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
                        clk_unregister(clk_table[i]);
        }
 
+       if (!IS_ERR(epll))
+               clk_disable_unprepare(epll);
+
        return 0;
 }