MLK-11495-02 ARM: imx: add busfreq support for imx6sl
authorBai Ping <b51503@freescale.com>
Sun, 6 Sep 2015 14:05:58 +0000 (22:05 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:29 +0000 (14:48 -0500)
Add busfreq support for i.MX6SL SOC. we support three
busfreq mode (high_bus_freq_mode/low_bus_freq_mode and
audio_bus_freq_mode).

Signed-off-by: Bai Ping <b51503@freescale.com>
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/busfreq-imx.c
arch/arm/mach-imx/busfreq_lpddr2.c
arch/arm/mach-imx/lpddr2_freq_imx6.S [new file with mode: 0644]
arch/arm/mach-imx/mach-imx6sl.c
drivers/clk/imx/clk-imx6sl.c

index b2a6902..1773558 100644 (file)
@@ -83,7 +83,8 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
 endif
 AFLAGS_ddr3_freq_imx6.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6Q) += mach-imx6q.o ddr3_freq_imx6.o
-obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o
+AFLAGS_lpddr2_freq_imx6.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o lpddr2_freq_imx6.o
 AFLAGS_ddr3_freq_imx6sx.o :=-Wa,-march=armv7-a
 AFLAGS_lpddr2_freq_imx6sx.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o ddr3_freq_imx6sx.o lpddr2_freq_imx6sx.o busfreq_lpddr2.o
index 36dba4c..885ae11 100644 (file)
@@ -58,6 +58,7 @@ static int bus_freq_scaling_is_active;
 static int high_bus_count, med_bus_count, audio_bus_count, low_bus_count;
 static unsigned int ddr_low_rate;
 static int cur_bus_freq_mode;
+static u32 org_arm_rate;
 
 extern unsigned long iram_tlb_phys_addr;
 extern int unsigned long iram_tlb_base_addr;
@@ -91,6 +92,14 @@ static struct clk *pll3_clk;
 static struct clk *step_clk;
 static struct clk *mmdc_clk;
 static struct clk *ocram_clk;
+static struct clk *pll1_clk;
+static struct clk *pll1_bypass_clk;
+static struct clk *pll1_bypass_src_clk;
+static struct clk *pll1_sys_clk;
+static struct clk *pll1_sw_clk;
+static struct clk *pll2_bypass_src_clk;
+static struct clk *pll2_bypass_clk;
+static struct clk *pll2_clk;
 static struct clk *pll2_400_clk;
 static struct clk *pll2_200_clk;
 static struct clk *pll2_bus_clk;
@@ -309,6 +318,175 @@ static void exit_lpm_imx6_smp(void)
                clk_disable_unprepare(pll2_400_clk);
 }
 
+static void enter_lpm_imx6sl(void)
+{
+       if (high_bus_freq_mode) {
+               /* Set periph_clk to be sourced from OSC_CLK */
+               clk_set_parent(periph_clk2_sel_clk, osc_clk);
+               clk_set_parent(periph_clk, periph_clk2_clk);
+               /* Ensure AHB/AXI clks are at 24MHz. */
+               clk_set_rate(ahb_clk, LPAPM_CLK);
+               clk_set_rate(ocram_clk, LPAPM_CLK);
+       }
+       if (audio_bus_count) {
+               /* Set AHB to 8MHz to lower pwer.*/
+               clk_set_rate(ahb_clk, LPAPM_CLK / 3);
+
+               /* Set up DDR to 100MHz. */
+               update_lpddr2_freq(HIGH_AUDIO_CLK);
+
+               /* Fix the clock tree in kernel */
+               clk_set_parent(periph2_pre_clk, pll2_200_clk);
+               clk_set_parent(periph2_clk, periph2_pre_clk);
+
+               if (low_bus_freq_mode || ultra_low_bus_freq_mode) {
+                       /*
+                        * Fix the clock tree in kernel, make sure
+                        * pll2_bypass is updated as it is
+                        * sourced from PLL2.
+                        */
+                       clk_set_parent(pll2_bypass_clk, pll2_clk);
+                       /*
+                        * Swtich ARM to run off PLL2_PFD2_400MHz
+                        * since DDR is anyway at 100MHz.
+                        */
+                       clk_set_parent(step_clk, pll2_400_clk);
+                       clk_set_parent(pll1_sw_clk, step_clk);
+
+                       /*
+                        * Ensure that the clock will be
+                        * at original speed.
+                        */
+                       clk_set_rate(arm_clk, org_arm_rate);
+               }
+               low_bus_freq_mode = 0;
+               ultra_low_bus_freq_mode = 0;
+               audio_bus_freq_mode = 1;
+               cur_bus_freq_mode = BUS_FREQ_AUDIO;
+       } else {
+               u32 arm_div, pll1_rate;
+               org_arm_rate = clk_get_rate(arm_clk);
+               if (low_bus_freq_mode && low_bus_count == 0) {
+                       /*
+                        * We are already in DDR @ 24MHz state, but
+                        * no one but ARM needs the DDR. In this case,
+                        * we can lower the DDR freq to 1MHz when ARM
+                        * enters WFI in this state. Keep track of this state.
+                        */
+                       ultra_low_bus_freq_mode = 1;
+                       low_bus_freq_mode = 0;
+                       audio_bus_freq_mode = 0;
+                       cur_bus_freq_mode = BUS_FREQ_ULTRA_LOW;
+               } else {
+                       if (!ultra_low_bus_freq_mode && !low_bus_freq_mode) {
+                               /*
+                                * Anyway, make sure the AHB is running at 24MHz
+                                * in low_bus_freq_mode.
+                                */
+                               if (audio_bus_freq_mode)
+                                       clk_set_rate(ahb_clk, LPAPM_CLK);
+                               /*
+                                * Set DDR to 24MHz.
+                                * Since we are going to bypass PLL2,
+                                * we need to move ARM clk off PLL2_PFD2
+                                * to PLL1. Make sure the PLL1 is running
+                                * at the lowest possible freq.
+                                * To work well with CPUFREQ we want to ensure that
+                                * the CPU freq does not change, so attempt to
+                                * get a freq as close to 396MHz as possible.
+                                */
+                               clk_set_rate(pll1_clk,
+                                       clk_round_rate(pll1_clk, (org_arm_rate * 2)));
+                               pll1_rate = clk_get_rate(pll1_clk);
+                               arm_div = pll1_rate / org_arm_rate;
+                               if (pll1_rate / arm_div > org_arm_rate)
+                                       arm_div++;
+                               /*
+                                 * Need to ensure that PLL1 is bypassed and enabled
+                                 * before ARM-PODF is set.
+                                 */
+                               clk_set_parent(pll1_bypass_clk, pll1_clk);
+                               /*
+                                * Ensure ARM CLK is lower before
+                                * changing the parent.
+                                */
+                               clk_set_rate(arm_clk, org_arm_rate / arm_div);
+                               /* Now set the ARM clk parent to PLL1_SYS. */
+                               clk_set_parent(pll1_sw_clk, pll1_sys_clk);
+
+                               /*
+                                * Set STEP_CLK back to OSC to save power and
+                                * also to maintain the parent.The WFI iram code
+                                * will switch step_clk to osc, but the clock API
+                                * is not aware of the change and when a new request
+                                * to change the step_clk parent to pll2_pfd2_400M
+                                * is requested sometime later, the change is ignored.
+                                */
+                               clk_set_parent(step_clk, osc_clk);
+                               /* Now set DDR to 24MHz. */
+                               update_lpddr2_freq(LPAPM_CLK);
+
+                               /*
+                                * Fix the clock tree in kernel.
+                                * Make sure PLL2 rate is updated as it gets
+                                * bypassed in the DDR freq change code.
+                                */
+                               clk_set_parent(pll2_bypass_clk, pll2_bypass_src_clk);
+                               clk_set_parent(periph2_clk2_sel_clk, pll2_bus_clk);
+                               clk_set_parent(periph2_clk, periph2_clk2_clk);
+                       }
+                       if (low_bus_count == 0) {
+                               ultra_low_bus_freq_mode = 1;
+                               low_bus_freq_mode = 0;
+                               cur_bus_freq_mode = BUS_FREQ_ULTRA_LOW;
+                       } else {
+                               ultra_low_bus_freq_mode = 0;
+                               low_bus_freq_mode = 1;
+                               cur_bus_freq_mode = BUS_FREQ_LOW;
+                       }
+                       audio_bus_freq_mode = 0;
+               }
+       }
+}
+
+static void exit_lpm_imx6sl(void)
+{
+       /* Change DDR freq in IRAM. */
+       update_lpddr2_freq(ddr_normal_rate);
+
+       /*
+        * Fix the clock tree in kernel.
+        * Make sure PLL2 rate is updated as it gets
+        * un-bypassed in the DDR freq change code.
+        */
+       clk_set_parent(pll2_bypass_clk, pll2_clk);
+       clk_set_parent(periph2_pre_clk, pll2_400_clk);
+       clk_set_parent(periph2_clk, periph2_pre_clk);
+
+       /* Ensure that periph_clk is sourced from PLL2_400. */
+       clk_set_parent(periph_pre_clk, pll2_400_clk);
+       /*
+        * Before switching the perhiph_clk, ensure that the
+        * AHB/AXI will not be too fast.
+        */
+       clk_set_rate(ahb_clk, LPAPM_CLK / 3);
+       clk_set_rate(ocram_clk, LPAPM_CLK / 2);
+       clk_set_parent(periph_clk, periph_pre_clk);
+
+       if (low_bus_freq_mode || ultra_low_bus_freq_mode) {
+               /* Move ARM from PLL1_SW_CLK to PLL2_400. */
+               clk_set_parent(step_clk, pll2_400_clk);
+               clk_set_parent(pll1_sw_clk, step_clk);
+               /*
+                 * Need to ensure that PLL1 is bypassed and enabled
+                 * before ARM-PODF is set.
+                 */
+               clk_set_parent(pll1_bypass_clk, pll1_bypass_src_clk);
+               clk_set_rate(arm_clk, org_arm_rate);
+               ultra_low_bus_freq_mode = 0;
+       }
+}
+
 static void enter_lpm_imx7d(void)
 {
        if (audio_bus_count) {
@@ -369,6 +547,8 @@ static void reduce_bus_freq(void)
                enter_lpm_imx6_up();
        else if (cpu_is_imx6q() || cpu_is_imx6dl())
                enter_lpm_imx6_smp();
+       else if (cpu_is_imx6sl())
+               enter_lpm_imx6sl();
 
        med_bus_freq_mode = 0;
        high_bus_freq_mode = 0;
@@ -460,6 +640,8 @@ static int set_high_bus_freq(int high_bus_freq)
                exit_lpm_imx6_up();
        else if (cpu_is_imx6q() || cpu_is_imx6dl())
                exit_lpm_imx6_smp();
+       else if (cpu_is_imx6sl())
+               exit_lpm_imx6sl();
 
        high_bus_freq_mode = 1;
        med_bus_freq_mode = 0;
@@ -809,18 +991,17 @@ static int busfreq_probe(struct platform_device *pdev)
                }
        }
 
-       if (cpu_is_imx6sx() || cpu_is_imx6ul()) {
+       if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6sl()) {
                ahb_clk = devm_clk_get(&pdev->dev, "ahb");
                ocram_clk = devm_clk_get(&pdev->dev, "ocram");
-               mmdc_clk = devm_clk_get(&pdev->dev, "mmdc");
                periph2_clk = devm_clk_get(&pdev->dev, "periph2");
                periph2_pre_clk = devm_clk_get(&pdev->dev, "periph2_pre");
                periph2_clk2_clk = devm_clk_get(&pdev->dev, "periph2_clk2");
                periph2_clk2_sel_clk =
                        devm_clk_get(&pdev->dev, "periph2_clk2_sel");
                if (IS_ERR(ahb_clk) || IS_ERR(ocram_clk)
-                       || IS_ERR(mmdc_clk) || IS_ERR(periph2_clk)
-                       || IS_ERR(periph2_pre_clk) || IS_ERR(periph2_clk2_clk)
+                       || IS_ERR(periph2_clk) || IS_ERR(periph2_pre_clk)
+                       || IS_ERR(periph2_clk2_clk)
                        || IS_ERR(periph2_clk2_sel_clk)) {
                        dev_err(busfreq_dev,
                                "%s: failed to get busfreq clk for imx6ul/sx/sl.\n", __func__);
@@ -828,6 +1009,15 @@ static int busfreq_probe(struct platform_device *pdev)
                }
        }
 
+       if (cpu_is_imx6sx() || cpu_is_imx6ul()) {
+               mmdc_clk = devm_clk_get(&pdev->dev, "mmdc");
+               if (IS_ERR(mmdc_clk)) {
+                       dev_err(busfreq_dev,
+                               "%s: failed to get mmdc clk for imx6sx/ul.\n", __func__);
+                       return -EINVAL;
+               }
+       }
+
        if (cpu_is_imx6sx()) {
                m4_clk = devm_clk_get(&pdev->dev, "m4");
                if (IS_ERR(m4_clk)) {
@@ -839,7 +1029,19 @@ static int busfreq_probe(struct platform_device *pdev)
        if (cpu_is_imx6sl()) {
                arm_clk = devm_clk_get(&pdev->dev, "arm");
                step_clk = devm_clk_get(&pdev->dev, "step");
-               if (IS_ERR(arm_clk) || IS_ERR(step_clk)) {
+               pll1_clk = devm_clk_get(&pdev->dev, "pll1");
+               pll1_bypass_src_clk = devm_clk_get(&pdev->dev, "pll1_bypass_src");
+               pll1_bypass_clk = devm_clk_get(&pdev->dev, "pll1_bypass");
+               pll1_sys_clk = devm_clk_get(&pdev->dev, "pll1_sys");
+               pll1_sw_clk = devm_clk_get(&pdev->dev, "pll1_sw");
+               pll2_bypass_src_clk = devm_clk_get(&pdev->dev, "pll2_bypass_src");
+               pll2_bypass_clk = devm_clk_get(&pdev->dev, "pll2_bypass");
+               pll2_clk = devm_clk_get(&pdev->dev, "pll2");
+               if (IS_ERR(arm_clk) || IS_ERR(step_clk) || IS_ERR(pll1_clk)
+                       || IS_ERR(pll1_bypass_src_clk) || IS_ERR(pll1_bypass_clk)
+                       || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk)
+                       || IS_ERR(pll2_bypass_src_clk) || IS_ERR(pll2_bypass_clk)
+                       || IS_ERR(pll2_clk)) {
                        dev_err(busfreq_dev,
                                "%s failed to get busfreq clk for imx6sl.\n", __func__);
                        return -EINVAL;
@@ -935,6 +1137,10 @@ static int busfreq_probe(struct platform_device *pdev)
                        err = init_mmdc_lpddr2_settings(pdev);
        } else if (cpu_is_imx6q() || cpu_is_imx6dl()) {
                err = init_mmdc_ddr3_settings_imx6_smp(pdev);
+       } else if (cpu_is_imx6q() || cpu_is_imx6dl()) {
+               err = init_mmdc_ddr3_settings_imx6_smp(pdev);
+       } else if (cpu_is_imx6sl()) {
+               err = init_mmdc_lpddr2_settings(pdev);
        }
 
        if (cpu_is_imx6sx()) {
index f262ccc..076ec84 100644 (file)
@@ -97,6 +97,10 @@ int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev)
 
        ddr_code_size = SZ_4K;
 
+       if (cpu_is_imx6sl())
+               mx6_change_lpddr2_freq = (void *)fncpy(
+                       (void *)ddr_freq_change_iram_base,
+                       &mx6_lpddr2_freq_change, ddr_code_size);
        if (cpu_is_imx6sx() || cpu_is_imx6ul())
                mx6_change_lpddr2_freq = (void *)fncpy(
                        (void *)ddr_freq_change_iram_base,
diff --git a/arch/arm/mach-imx/lpddr2_freq_imx6.S b/arch/arm/mach-imx/lpddr2_freq_imx6.S
new file mode 100644 (file)
index 0000000..2e7843c
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include "hardware.h"
+
+.globl imx6_lpddr2_freq_change_start
+.globl imx6_lpddr2_freq_change_end
+
+       .macro    mx6sl_switch_to_24MHz
+
+       /*
+        * Set MMDC clock to be sourced from PLL3.
+        * Ensure first periph2_clk2 is sourced from PLL3.
+        * Set the PERIPH2_CLK2_PODF to divide by 2.
+        */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x7
+       orr     r6, r6, #0x1
+       str     r6, [r2, #0x14]
+
+       /* Select PLL3 to source MMDC. */
+       ldr     r6, [r2, #0x18]
+       bic     r6, r6, #0x100000
+       str     r6, [r2, #0x18]
+
+       /* Swtich periph2_clk_sel to run from PLL3. */
+       ldr     r6, [r2, #0x14]
+       orr     r6, r6, #0x4000000
+       str     r6, [r2, #0x14]
+
+periph2_clk_switch1:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     periph2_clk_switch1
+
+       /*
+        * Need to clock gate the 528 PFDs before
+        * powering down PLL2.
+        * Only the PLL2_PFD2_400M should be ON
+        * at this time, so only clock gate that one.
+        */
+       ldr     r6, [r3, #0x100]
+       orr     r6, r6, #0x800000
+       str     r6, [r3, #0x100]
+
+       /*
+        * Set PLL2 to bypass state. We should be here
+        * only if MMDC is not sourced from PLL2.
+        */
+       ldr     r6, [r3, #0x30]
+       orr     r6, r6, #0x10000
+       str     r6, [r3, #0x30]
+
+       ldr     r6, [r3, #0x30]
+       orr     r6, r6, #0x1000
+       str     r6, [r3, #0x30]
+
+       /* Ensure pre_periph2_clk_mux is set to pll2 */
+       ldr     r6, [r2, #0x18]
+       bic     r6, r6, #0x600000
+       str     r6, [r2, #0x18]
+
+       /* Set MMDC clock to be sourced from the bypassed PLL2. */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x4000000
+       str     r6, [r2, #0x14]
+
+periph2_clk_switch2:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     periph2_clk_switch2
+
+       /*
+        * Now move MMDC back to periph2_clk2 source.
+        * after selecting PLL2 as the option.
+        * Select PLL2 as the source.
+        */
+       ldr     r6, [r2, #0x18]
+       orr     r6, r6, #0x100000
+       str     r6, [r2, #0x18]
+
+       /* set periph2_clk2_podf to divide by 1. */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x7
+       str     r6, [r2, #0x14]
+
+       /* Now move periph2_clk to periph2_clk2 source */
+       ldr     r6, [r2, #0x14]
+       orr     r6, r6, #0x4000000
+       str     r6, [r2, #0x14]
+
+periph2_clk_switch3:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     periph2_clk_switch3
+
+       /* Now set the MMDC PODF back to 1.*/
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x38
+       str     r6, [r2, #0x14]
+
+mmdc_podf0:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     mmdc_podf0
+
+       .endm
+
+         .macro        ddr_switch_400MHz
+
+       /* Set MMDC divider first, in case PLL3 is at 480MHz. */
+       ldr     r6, [r3, #0x10]
+       and     r6, r6, #0x10000
+       cmp     r6, #0x10000
+       beq     pll3_in_bypass
+
+       /* Set MMDC divder to divide by 2. */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x38
+       orr     r6, r6, #0x8
+       str     r6, [r2, #0x14]
+
+mmdc_podf:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     mmdc_podf
+
+pll3_in_bypass:
+       /*
+        * Check if we are switching between
+        * 400Mhz <-> 100MHz.If so, we should
+        * try to source MMDC from PLL2_200M.
+        */
+       cmp     r1, #0
+       beq     not_low_bus_freq
+
+       /* Ensure that MMDC is sourced from PLL2 mux first. */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x4000000
+       str     r6, [r2, #0x14]
+
+periph2_clk_switch4:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     periph2_clk_switch4
+
+not_low_bus_freq:
+       /* Now ensure periph2_clk2_sel mux is set to PLL3 */
+       ldr     r6, [r2, #0x18]
+       bic     r6, r6, #0x100000
+       str     r6, [r2, #0x18]
+
+       /* Now switch MMDC to PLL3. */
+       ldr     r6, [r2, #0x14]
+       orr     r6, r6, #0x4000000
+       str     r6, [r2, #0x14]
+
+periph2_clk_switch5:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     periph2_clk_switch5
+
+       /*
+        * Check if PLL2 is already unlocked.
+        * If so do nothing with PLL2.
+        */
+       cmp     r1, #0
+       beq     pll2_already_on
+
+       /* Now power up PLL2 and unbypass it. */
+       ldr     r6, [r3, #0x30]
+       bic     r6, r6, #0x1000
+       str     r6, [r3, #0x30]
+
+       /* Make sure PLL2 has locked.*/
+wait_for_pll_lock:
+       ldr     r6, [r3, #0x30]
+       and     r6, r6, #0x80000000
+       cmp     r6, #0x80000000
+       bne     wait_for_pll_lock
+
+       ldr     r6, [r3, #0x30]
+       bic     r6, r6, #0x10000
+       str     r6, [r3, #0x30]
+
+       /*
+        * Need to enable the 528 PFDs after
+        * powering up PLL2.
+        * Only the PLL2_PFD2_400M should be ON
+        * as it feeds the MMDC. Rest should have
+        * been managed by clock code.
+        */
+       ldr     r6, [r3, #0x100]
+       bic     r6, r6, #0x800000
+       str     r6, [r3, #0x100]
+
+pll2_already_on:
+       /*
+        * Now switch MMDC clk back to pll2_mux option.
+        * Ensure pre_periph2_clk2 is set to pll2_pfd_400M.
+        * If switching to audio DDR freq, set the
+        * pre_periph2_clk2 to PLL2_PFD_200M
+        */
+       ldr     r6, =400000000
+       cmp     r6, r0
+       bne     use_pll2_pfd_200M
+
+       ldr     r6, [r2, #0x18]
+       bic     r6, r6, #0x600000
+       orr     r6, r6, #0x200000
+       str     r6, [r2, #0x18]
+       ldr     r6, =400000000
+       b       cont2
+
+use_pll2_pfd_200M:
+       ldr     r6, [r2, #0x18]
+       orr     r6, r6, #0x600000
+       str     r6, [r2, #0x18]
+       ldr     r6, =200000000
+
+cont2:
+       ldr     r4, [r2, #0x14]
+       bic     r4, r4, #0x4000000
+       str     r4, [r2, #0x14]
+
+periph2_clk_switch6:
+       ldr     r4, [r2, #0x48]
+       cmp     r4, #0
+       bne     periph2_clk_switch6
+
+change_divider_only:
+       /*
+        * Calculate the MMDC divider
+        * based on the requested freq.
+        */
+       ldr     r4, =0
+Loop2:
+       sub     r6, r6, r0
+       cmp     r6, r0
+       blt     Div_Found
+       add     r4, r4, #1
+       bgt     Loop2
+
+       /* Shift divider into correct offset. */
+       lsl     r4, r4, #3
+Div_Found:
+       /* Set the MMDC PODF. */
+       ldr     r6, [r2, #0x14]
+       bic     r6, r6, #0x38
+       orr     r6, r6, r4
+       str     r6, [r2, #0x14]
+
+mmdc_podf1:
+       ldr     r6, [r2, #0x48]
+       cmp     r6, #0
+       bne     mmdc_podf1
+
+       .endm
+
+       .macro  mmdc_clk_lower_100MHz
+
+       /*
+        * Prior to reducing the DDR frequency (at 528/400 MHz),
+        * read the Measure unit count bits (MU_UNIT_DEL_NUM)
+        */
+       ldr     r5, =0x8B8
+       ldr     r6, [r8, r5]
+       /* Original MU unit count */
+       mov     r6, r6, LSR #16
+       ldr     r4, =0x3FF
+       and     r6, r6, r4
+       /* Original MU unit count * 2 */
+       mov     r7, r6, LSL #1
+       /*
+        * Bypass the automatic measure unit when below 100 MHz
+        * by setting the Measure unit bypass enable bit (MU_BYP_EN)
+        */
+       ldr     r6, [r8, r5]
+       orr     r6, r6, #0x400
+       str     r6, [r8, r5]
+       /*
+        * Double the measure count value read in step 1 and program it in the
+        * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit
+        * Register for the reduced frequency operation below 100 MHz
+        */
+       ldr     r6, [r8, r5]
+       ldr     r4, =0x3FF
+       bic     r6, r6, r4
+       orr     r6, r6, r7
+       str     r6, [r8, r5]
+       /* Now perform a Force Measurement. */
+       ldr     r6, [r8, r5]
+       orr     r6, r6, #0x800
+       str     r6, [r8, r5]
+       /* Wait for FRC_MSR to clear. */
+force_measure:
+       ldr     r6, [r8, r5]
+       and     r6, r6, #0x800
+       cmp     r6, #0x0
+       bne     force_measure
+
+       .endm
+
+       .macro  mmdc_clk_above_100MHz
+
+       /* Make sure that the PHY measurement unit is NOT in bypass mode */
+       ldr     r5, =0x8B8
+       ldr     r6, [r8, r5]
+       bic     r6, r6, #0x400
+       str     r6, [r8, r5]
+       /* Now perform a Force Measurement. */
+       ldr     r6, [r8, r5]
+       orr     r6, r6, #0x800
+       str     r6, [r8, r5]
+       /* Wait for FRC_MSR to clear. */
+force_measure1:
+       ldr     r6, [r8, r5]
+       and     r6, r6, #0x800
+       cmp     r6, #0x0
+       bne     force_measure1
+       .endm
+
+/*
+ *  mx6_lpddr2_freq_change
+ *
+ *  Make sure DDR is in self-refresh.
+ *  IRQs are already disabled.
+ * r0 : DDR freq.
+ * r1: low_bus_freq_mode flag
+ */
+       .align 3
+ENTRY(mx6_lpddr2_freq_change)
+imx6_lpddr2_freq_change_start:
+       push {r4-r10}
+
+       /*
+        * To ensure no page table walks occur in DDR, we
+        * have a another page table stored in IRAM that only
+        * contains entries pointing to IRAM, AIPS1 and AIPS2.
+        * We need to set the TTBR1 to the new IRAM TLB.
+        * Do the following steps:
+        * 1. Flush the Branch Target Address Cache (BTAC)
+        * 2. Set TTBR1 to point to IRAM page table.
+        * 3. Disable page table walks in TTBR0 (PD0 = 1)
+        * 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
+        *     and 2-4G is translated by TTBR1.
+        */
+
+       ldr     r6, =iram_tlb_phys_addr
+       ldr     r7, [r6]
+
+       /* Disable Branch Prediction, Z bit in SCTLR. */
+       mrc     p15, 0, r6, c1, c0, 0
+       bic     r6, r6, #0x800
+       mcr     p15, 0, r6, c1, c0, 0
+
+       /* Flush the Branch Target Address Cache (BTAC) */
+       ldr     r6, =0x0
+       mcr     p15, 0, r6, c7, c1, 6
+
+       dsb
+       isb
+       /* Store the IRAM table in TTBR1 */
+       mcr     p15, 0, r7, c2, c0, 1
+
+       /* Read TTBCR and set PD0=1, N = 1 */
+       mrc     p15, 0, r6, c2, c0, 2
+       orr     r6, r6, #0x11
+       mcr     p15, 0, r6, c2, c0, 2
+
+       dsb
+       isb
+
+       /* flush the TLB */
+       ldr     r6, =0x0
+       mcr     p15, 0, r6, c8, c3, 0
+
+       /* Disable L1 data cache. */
+       mrc     p15, 0, r6, c1, c0, 0
+       bic     r6, r6, #0x4
+       mcr     p15, 0, r6, c1, c0, 0
+
+       dsb
+       isb
+
+
+#ifdef CONFIG_CACHE_L2X0
+       /*
+        * Need to make sure the buffers in L2 are drained.
+        * Performing a sync operation does this.
+        */
+       ldr     r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
+
+       /* Wait for background operations to complete. */
+wait_for_l2_to_idle:
+       ldr     r6, [r7, #0x730]
+       cmp     r6, #0x0
+       bne     wait_for_l2_to_idle
+
+       mov     r6, #0x0
+       str     r6, [r7, #0x730]
+
+       /*
+        * The second dsb might be needed to keep cache sync (device write)
+        * ordering with the memory accesses before it.
+        */
+       dsb
+       isb
+
+       /* Disable L2. */
+       str     r6, [r7, #0x100]
+#endif
+
+       ldr     r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
+       ldr     r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
+       ldr     r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
+
+       /* Disable Automatic power savings. */
+       ldr     r6, [r8, #0x404]
+       orr     r6, r6, #0x01
+       str     r6, [r8, #0x404]
+
+       /* MMDC0_MDPDC disable power down timer */
+       ldr     r6, [r8, #0x4]
+       bic     r6, r6, #0xff00
+       str     r6, [r8, #0x4]
+
+       /* Delay for a while */
+       ldr     r10, =10
+delay1:
+       ldr     r7, =0
+cont1:
+       ldr     r6, [r8, r7]
+       add     r7, r7, #4
+       cmp     r7, #16
+       bne     cont1
+       sub     r10, r10, #1
+       cmp     r10, #0
+       bgt     delay1
+
+       /* Make the DDR explicitly enter self-refresh. */
+       ldr     r6, [r8, #0x404]
+       orr     r6, r6, #0x200000
+       str     r6, [r8, #0x404]
+
+poll_dvfs_set_1:
+       ldr     r6, [r8, #0x404]
+       and     r6, r6, #0x2000000
+       cmp     r6, #0x2000000
+       bne     poll_dvfs_set_1
+
+       /* set SBS step-by-step mode */
+       ldr     r6, [r8, #0x410]
+       orr     r6, r6, #0x100
+       str     r6, [r8, #0x410]
+
+       ldr     r10, =100000000
+       cmp     r0, r10
+       bgt     set_ddr_mu_above_100
+       mmdc_clk_lower_100MHz
+
+set_ddr_mu_above_100:
+       ldr     r10, =24000000
+       cmp     r0, r10
+       beq     set_to_24MHz
+
+       ddr_switch_400MHz
+
+       ldr     r10,=100000000
+       cmp     r0, r10
+       blt     done
+       mmdc_clk_above_100MHz
+
+       b       done
+
+set_to_24MHz:
+       mx6sl_switch_to_24MHz
+
+done:
+       /* clear DVFS - exit from self refresh mode */
+       ldr     r6, [r8, #0x404]
+       bic     r6, r6, #0x200000
+       str     r6, [r8, #0x404]
+
+poll_dvfs_clear_1:
+       ldr     r6, [r8, #0x404]
+       and     r6, r6, #0x2000000
+       cmp     r6, #0x2000000
+       beq     poll_dvfs_clear_1
+
+       /* Enable Automatic power savings. */
+       ldr     r6, [r8, #0x404]
+       bic     r6, r6, #0x01
+       str     r6, [r8, #0x404]
+
+       ldr     r10, =24000000
+       cmp     r0, r10
+       beq     skip_power_down
+
+       /* Enable MMDC power down timer. */
+       ldr     r6, [r8, #0x4]
+       orr     r6, r6, #0x5500
+       str     r6, [r8, #0x4]
+
+skip_power_down:
+       /* clear SBS - unblock DDR accesses */
+       ldr     r6, [r8, #0x410]
+       bic     r6, r6, #0x100
+       str     r6, [r8, #0x410]
+
+#ifdef CONFIG_CACHE_L2X0
+       /* Enable L2. */
+       ldr     r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
+       ldr     r6, =0x1
+       str     r6, [r7, #0x100]
+#endif
+
+       /* Enable L1 data cache. */
+       mrc     p15, 0, r6, c1, c0, 0
+       orr     r6, r6, #0x4
+       mcr     p15, 0, r6, c1, c0, 0
+
+       /* Restore the TTBCR */
+       dsb
+       isb
+
+       /* Read TTBCR and set PD0=0, N = 0 */
+       mrc     p15, 0, r6, c2, c0, 2
+       bic     r6, r6, #0x11
+       mcr     p15, 0, r6, c2, c0, 2
+       dsb
+       isb
+
+       /* flush the TLB */
+       ldr     r6, =0x0
+       mcr     p15, 0, r6, c8, c3, 0
+
+       dsb
+       isb
+
+       /* Enable Branch Prediction, Z bit in SCTLR. */
+       mrc     p15, 0, r6, c1, c0, 0
+       orr     r6, r6, #0x800
+       mcr     p15, 0, r6, c1, c0, 0
+
+       /* Flush the Branch Target Address Cache (BTAC) */
+       ldr     r6, =0x0
+       mcr     p15, 0, r6, c7, c1, 6
+
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       pop {r4-r10}
+
+       /* Restore registers */
+       mov     pc, lr
+
+       /*
+        * Add ltorg here to ensure that all
+        * literals are stored here and are
+        * within the text space.
+        */
+       .ltorg
+imx6_lpddr2_freq_change_end:
index 215d73c..34e05cc 100644 (file)
@@ -78,6 +78,9 @@ static void __init imx6sl_map_io(void)
 {
        debug_ll_io_init();
        imx6_pm_map_io();
+#ifdef CONFIG_CPU_FREQ
+       imx_busfreq_map_io();
+#endif
 }
 
 static const char * const imx6sl_dt_compat[] __initconst = {
index 5600f25..869d693 100644 (file)
@@ -18,6 +18,8 @@
 #include "clk.h"
 
 #define CCSR                   0xc
+#define CCDR                   0x04
+#define CCDR_CH0_HS_BYP                17
 #define BM_CCSR_PLL1_SW_CLK_SEL        (1 << 2)
 #define CACRR                  0x10
 #define CDHIPR                 0x48
@@ -195,7 +197,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
 {
        struct device_node *np;
        void __iomem *base;
-       int i;
+       int i, reg;
        int ret;
 
        clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
@@ -227,7 +229,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
        clks[IMX6SL_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB,     "pll7", "osc", base + 0x20, 0x3);
 
        clks[IMX6SL_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT);
-       clks[IMX6SL_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
+       clks[IMX6SL_PLL2_BYPASS] = imx_clk_mux_flags_bus("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
        clks[IMX6SL_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT);
        clks[IMX6SL_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT);
        clks[IMX6SL_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT);
@@ -243,7 +245,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
        clk_set_parent(clks[IMX6SL_PLL6_BYPASS], clks[IMX6SL_CLK_PLL6]);
        clk_set_parent(clks[IMX6SL_PLL7_BYPASS], clks[IMX6SL_CLK_PLL7]);
 
-       clks[IMX6SL_CLK_PLL1_SYS]      = imx_clk_gate("pll1_sys",      "pll1_bypass", base + 0x00, 13);
+       clks[IMX6SL_CLK_PLL1_SYS]      = imx_clk_fixed_factor("pll1_sys", "pll1_bypass", 1, 1);
        clks[IMX6SL_CLK_PLL2_BUS]      = imx_clk_gate("pll2_bus",      "pll2_bypass", base + 0x30, 13);
        clks[IMX6SL_CLK_PLL3_USB_OTG]  = imx_clk_gate("pll3_usb_otg",  "pll3_bypass", base + 0x10, 13);
        clks[IMX6SL_CLK_PLL4_AUDIO]    = imx_clk_gate("pll4_audio",    "pll4_bypass", base + 0x70, 13);
@@ -331,7 +333,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
        clks[IMX6SL_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26,  1,   base + 0x48, 3,  periph2_sels, ARRAY_SIZE(periph2_sels));
 
        /*                                                   name                 parent_name          reg       shift width */
-       clks[IMX6SL_CLK_OCRAM_PODF]        = imx_clk_divider("ocram_podf",        "ocram_sel",         base + 0x14, 16, 3);
+       clks[IMX6SL_CLK_OCRAM_PODF]        = imx_clk_busy_divider("ocram_podf",        "ocram_sel",         base + 0x14, 16, 3, base + 0x48, 0);
        clks[IMX6SL_CLK_PERIPH_CLK2_PODF]  = imx_clk_divider("periph_clk2_podf",  "periph_clk2_sel",   base + 0x14, 27, 3);
        clks[IMX6SL_CLK_PERIPH2_CLK2_PODF] = imx_clk_divider("periph2_clk2_podf", "periph2_clk2_sel",  base + 0x14, 0,  3);
        clks[IMX6SL_CLK_IPG]               = imx_clk_divider("ipg",               "ahb",               base + 0x14, 8,  2);
@@ -421,6 +423,11 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
        clk_data.clk_num = ARRAY_SIZE(clks);
        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
 
+       /* Ensure the CH0 handshake is bypassed */
+       reg = readl_relaxed(base + CCDR);
+       reg |= 1 << CCDR_CH0_HS_BYP;
+       writel_relaxed(reg, base + CCDR);
+
        /* Ensure the AHB clk is at 132MHz. */
        ret = clk_set_rate(clks[IMX6SL_CLK_AHB], 132000000);
        if (ret)