|
|
@@ -18,6 +18,22 @@
|
|
|
#include "clk.h"
|
|
|
#include "common.h"
|
|
|
|
|
|
+#define CCSR 0xc
|
|
|
+#define BM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
|
|
|
+#define CACRR 0x10
|
|
|
+#define CDHIPR 0x48
|
|
|
+#define BM_CDHIPR_ARM_PODF_BUSY (1 << 16)
|
|
|
+#define ARM_WAIT_DIV_396M 2
|
|
|
+#define ARM_WAIT_DIV_792M 4
|
|
|
+#define ARM_WAIT_DIV_996M 6
|
|
|
+
|
|
|
+#define PLL_ARM 0x0
|
|
|
+#define BM_PLL_ARM_DIV_SELECT (0x7f << 0)
|
|
|
+#define BM_PLL_ARM_POWERDOWN (1 << 12)
|
|
|
+#define BM_PLL_ARM_ENABLE (1 << 13)
|
|
|
+#define BM_PLL_ARM_LOCK (1 << 31)
|
|
|
+#define PLL_ARM_DIV_792M 66
|
|
|
+
|
|
|
static const char *step_sels[] = { "osc", "pll2_pfd2", };
|
|
|
static const char *pll1_sw_sels[] = { "pll1_sys", "step", };
|
|
|
static const char *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", };
|
|
|
@@ -65,6 +81,8 @@ static struct clk_div_table video_div_table[] = {
|
|
|
|
|
|
static struct clk *clks[IMX6SL_CLK_END];
|
|
|
static struct clk_onecell_data clk_data;
|
|
|
+static void __iomem *ccm_base;
|
|
|
+static void __iomem *anatop_base;
|
|
|
|
|
|
static const u32 clks_init_on[] __initconst = {
|
|
|
IMX6SL_CLK_IPG, IMX6SL_CLK_ARM, IMX6SL_CLK_MMDC_ROOT,
|
|
|
@@ -81,19 +99,70 @@ static const u32 clks_init_on[] __initconst = {
|
|
|
* entering WAIT mode.
|
|
|
*
|
|
|
* This function will set the ARM clk to max value within the 12:5 limit.
|
|
|
+ * As IPG clock is fixed at 66MHz(so ARM freq must not exceed 158.4MHz),
|
|
|
+ * ARM freq are one of below setpoints: 396MHz, 792MHz and 996MHz, since
|
|
|
+ * the clk APIs can NOT be called in idle thread(may cause kernel schedule
|
|
|
+ * as there is sleep function in PLL wait function), so here we just slow
|
|
|
+ * down ARM to below freq according to previous freq:
|
|
|
+ *
|
|
|
+ * run mode wait mode
|
|
|
+ * 396MHz -> 132MHz;
|
|
|
+ * 792MHz -> 158.4MHz;
|
|
|
+ * 996MHz -> 142.3MHz;
|
|
|
*/
|
|
|
+static int imx6sl_get_arm_divider_for_wait(void)
|
|
|
+{
|
|
|
+ if (readl_relaxed(ccm_base + CCSR) & BM_CCSR_PLL1_SW_CLK_SEL) {
|
|
|
+ return ARM_WAIT_DIV_396M;
|
|
|
+ } else {
|
|
|
+ if ((readl_relaxed(anatop_base + PLL_ARM) &
|
|
|
+ BM_PLL_ARM_DIV_SELECT) == PLL_ARM_DIV_792M)
|
|
|
+ return ARM_WAIT_DIV_792M;
|
|
|
+ else
|
|
|
+ return ARM_WAIT_DIV_996M;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void imx6sl_enable_pll_arm(bool enable)
|
|
|
+{
|
|
|
+ static u32 saved_pll_arm;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (enable) {
|
|
|
+ saved_pll_arm = val = readl_relaxed(anatop_base + PLL_ARM);
|
|
|
+ val |= BM_PLL_ARM_ENABLE;
|
|
|
+ val &= ~BM_PLL_ARM_POWERDOWN;
|
|
|
+ writel_relaxed(val, anatop_base + PLL_ARM);
|
|
|
+ while (!(__raw_readl(anatop_base + PLL_ARM) & BM_PLL_ARM_LOCK))
|
|
|
+ ;
|
|
|
+ } else {
|
|
|
+ writel_relaxed(saved_pll_arm, anatop_base + PLL_ARM);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void imx6sl_set_wait_clk(bool enter)
|
|
|
{
|
|
|
- static unsigned long saved_arm_rate;
|
|
|
+ static unsigned long saved_arm_div;
|
|
|
+ int arm_div_for_wait = imx6sl_get_arm_divider_for_wait();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * According to hardware design, arm podf change need
|
|
|
+ * PLL1 clock enabled.
|
|
|
+ */
|
|
|
+ if (arm_div_for_wait == ARM_WAIT_DIV_396M)
|
|
|
+ imx6sl_enable_pll_arm(true);
|
|
|
|
|
|
if (enter) {
|
|
|
- unsigned long ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]);
|
|
|
- unsigned long max_arm_wait_rate = (12 * ipg_rate) / 5;
|
|
|
- saved_arm_rate = clk_get_rate(clks[IMX6SL_CLK_ARM]);
|
|
|
- clk_set_rate(clks[IMX6SL_CLK_ARM], max_arm_wait_rate);
|
|
|
+ saved_arm_div = readl_relaxed(ccm_base + CACRR);
|
|
|
+ writel_relaxed(arm_div_for_wait, ccm_base + CACRR);
|
|
|
} else {
|
|
|
- clk_set_rate(clks[IMX6SL_CLK_ARM], saved_arm_rate);
|
|
|
+ writel_relaxed(saved_arm_div, ccm_base + CACRR);
|
|
|
}
|
|
|
+ while (__raw_readl(ccm_base + CDHIPR) & BM_CDHIPR_ARM_PODF_BUSY)
|
|
|
+ ;
|
|
|
+
|
|
|
+ if (arm_div_for_wait == ARM_WAIT_DIV_396M)
|
|
|
+ imx6sl_enable_pll_arm(false);
|
|
|
}
|
|
|
|
|
|
static void __init imx6sl_clocks_init(struct device_node *ccm_node)
|
|
|
@@ -111,6 +180,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
|
|
|
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop");
|
|
|
base = of_iomap(np, 0);
|
|
|
WARN_ON(!base);
|
|
|
+ anatop_base = base;
|
|
|
|
|
|
/* type name parent base div_mask */
|
|
|
clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
|
|
|
@@ -158,6 +228,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
|
|
|
np = ccm_node;
|
|
|
base = of_iomap(np, 0);
|
|
|
WARN_ON(!base);
|
|
|
+ ccm_base = base;
|
|
|
|
|
|
/* Reuse imx6q pm code */
|
|
|
imx6q_pm_set_ccm_base(base);
|