MLK-9708 arm: imx: add low power idle support for i.mx6sx
authorAnson Huang <b20788@freescale.com>
Mon, 20 Oct 2014 08:16:20 +0000 (16:16 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:04 +0000 (14:48 -0500)
Enable low power idle for i.MX6SX:

   WFI            -> first level idle;
   WAIT mode      -> second level idle;
   Low power idle -> third level idle, only when system is in low bus mode.

In low powe idle mode, below operations will be done:

   ARM power off;
   AHB freq lower to 3MHz;
   PERCLK freq lower to 6MHz;
   MMDC freq lower to 1MHz;

Anatop will be put into low power mode, and regular band-gap will
be off and low power band-gap will be enabled instead.

Also, in low power idle mode, 24MHz XTAL power will be off and 24MHz clk
source will be switched to RC-OSC to save power, this feature is only
enabled on i.MX6SX TO1.2.

This patch is cherry-picked from L3.14.y, it is the latest version, below
conflicts are fixed.

Signed-off-by: Anson Huang <b20788@freescale.com>
Conflicts:
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/common.h
arch/arm/mach-imx/cpuidle-imx6sx.c
arch/arm/mach-imx/cpuidle.h
arch/arm/mach-imx/mach-imx6sx.c
arch/arm/mach-imx/pm-imx6.c

arch/arm/mach-imx/Makefile
arch/arm/mach-imx/common.h
arch/arm/mach-imx/cpuidle-imx6sx.c
arch/arm/mach-imx/imx6sx_low_power_idle.S [new file with mode: 0644]
arch/arm/mach-imx/mach-imx6sx.c
drivers/clk/imx/clk-imx6sx.c

index 7ff16f3..fa37674 100644 (file)
@@ -26,7 +26,8 @@ ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
 obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
 obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
-obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o
+AFLAGS_imx6sx_low_power_idle.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o imx6sx_low_power_idle.o
 obj-$(CONFIG_SOC_IMX6UL) += cpuidle-imx6sx.o
 AFLAGS_imx7d_low_power_idle.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX7D) += cpuidle-imx7d.o imx7d_low_power_idle.o
index 4719b3f..9cba42f 100644 (file)
@@ -125,6 +125,7 @@ void imx_cpu_die(unsigned int cpu);
 int imx_cpu_kill(unsigned int cpu);
 void imx_busfreq_map_io(void);
 void imx7d_low_power_idle(void);
+void imx6sx_low_power_idle(void);
 
 #ifdef CONFIG_SUSPEND
 void v7_cpu_resume(void);
index c5a5c3a..c1c7ad4 100644 (file)
@@ -1,22 +1,92 @@
 /*
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
 
+#include <linux/busfreq-imx.h>
 #include <linux/cpuidle.h>
 #include <linux/cpu_pm.h>
+#include <linux/delay.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <asm/cacheflush.h>
 #include <asm/cpuidle.h>
+#include <asm/fncpy.h>
+#include <asm/mach/map.h>
+#include <asm/proc-fns.h>
 #include <asm/suspend.h>
+#include <asm/tlb.h>
 
 #include "common.h"
 #include "cpuidle.h"
+#include "hardware.h"
 
-static int imx6sx_idle_finish(unsigned long val)
+#define MX6_MAX_MMDC_IO_NUM    19
+
+#define PMU_LOW_PWR_CTRL       0x270
+#define XTALOSC24M_OSC_CONFIG0 0x2a0
+#define XTALOSC24M_OSC_CONFIG1 0x2b0
+#define XTALOSC24M_OSC_CONFIG2 0x2c0
+#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT   24
+#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK         0xf
+#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT                16
+#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK          0xf
+#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT         12
+#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT       4
+#define XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT            1
+#define XTALOSC24M_OSC_CONFIG0_START_SHIFT             0
+#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT      20
+#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT      0
+#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK       0xfff
+#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT      0
+
+extern unsigned long iram_tlb_phys_addr;
+
+static void __iomem *wfi_iram_base;
+#ifdef CONFIG_CPU_FREQ
+static void __iomem *wfi_iram_base_phys;
+extern unsigned long mx6sx_lpm_wfi_start asm("mx6sx_lpm_wfi_start");
+extern unsigned long mx6sx_lpm_wfi_end asm("mx6sx_lpm_wfi_end");
+#endif
+
+struct imx6_pm_base {
+       phys_addr_t pbase;
+       void __iomem *vbase;
+};
+
+static const u32 imx6sx_mmdc_io_offset[] __initconst = {
+       0x2ec, 0x2f0, 0x2f4, 0x2f8, /* DQM0 ~ DQM3 */
+       0x330, 0x334, 0x338, 0x33c, /* SDQS0 ~ SDQS3 */
+       0x60c, 0x610, 0x61c, 0x620, /* B0DS ~ B3DS */
+       0x5f8, 0x608, 0x310, 0x314, /* CTL, MODE, SODT0, SODT1 */
+       0x300, 0x2fc, 0x32c,        /* CAS, RAS, SDCLK_0 */
+};
+
+struct imx6_cpuidle_pm_info {
+       phys_addr_t pbase; /* The physical address of pm_info. */
+       phys_addr_t resume_addr; /* The physical resume address for asm code */
+       u32 pm_info_size; /* Size of pm_info. */
+       u32 ttbr;
+       struct imx6_pm_base mmdc_base;
+       struct imx6_pm_base iomuxc_base;
+       struct imx6_pm_base ccm_base;
+       struct imx6_pm_base gpc_base;
+       struct imx6_pm_base l2_base;
+       struct imx6_pm_base anatop_base;
+       struct imx6_pm_base src_base;
+       struct imx6_pm_base sema4_base;
+       u32 saved_diagnostic; /* To save disagnostic register */
+       u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
+       u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
+} __aligned(8);
+
+static void (*imx6sx_wfi_in_iram_fn)(void __iomem *iram_vbase);
+
+static int imx6_idle_finish(unsigned long val)
 {
        /*
         * for Cortex-A7 which has an internal L2
@@ -27,7 +97,7 @@ static int imx6sx_idle_finish(unsigned long val)
         * just call flush_cache_all() is fine.
         */
        flush_cache_all();
-       cpu_do_idle();
+       imx6sx_wfi_in_iram_fn(wfi_iram_base);
 
        return 0;
 }
@@ -35,29 +105,21 @@ static int imx6sx_idle_finish(unsigned long val)
 static int imx6sx_enter_wait(struct cpuidle_device *dev,
                            struct cpuidle_driver *drv, int index)
 {
-       imx6_set_lpm(WAIT_UNCLOCKED);
+       int mode = get_bus_freq_mode();
 
-       switch (index) {
-       case 1:
+       imx6_set_lpm(WAIT_UNCLOCKED);
+       if ((index == 1) || ((mode != BUS_FREQ_LOW) && index == 2)) {
                cpu_do_idle();
-               break;
-       case 2:
-               imx6_enable_rbc(true);
-               imx_gpc_set_arm_power_in_lpm(true);
-               imx_set_cpu_jump(0, v7_cpu_resume);
-               /* Need to notify there is a cpu pm operation. */
-               cpu_pm_enter();
-               cpu_cluster_pm_enter();
-
-               cpu_suspend(0, imx6sx_idle_finish);
-
-               cpu_cluster_pm_exit();
-               cpu_pm_exit();
-               imx_gpc_set_arm_power_in_lpm(false);
-               imx6_enable_rbc(false);
-               break;
-       default:
-               break;
+       } else {
+                       /* Need to notify there is a cpu pm operation. */
+                       cpu_pm_enter();
+                       cpu_cluster_pm_enter();
+
+                       cpu_suspend(0, imx6_idle_finish);
+
+                       cpu_cluster_pm_exit();
+                       cpu_pm_exit();
+                       imx6_enable_rbc(false);
        }
 
        imx6_set_lpm(WAIT_CLOCKED);
@@ -71,24 +133,23 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
        .states = {
                /* WFI */
                ARM_CPUIDLE_WFI_STATE,
-               /* WAIT */
+               /* WAIT MODE */
                {
                        .exit_latency = 50,
                        .target_residency = 75,
-                       .flags = CPUIDLE_FLAG_TIMER_STOP,
                        .enter = imx6sx_enter_wait,
                        .name = "WAIT",
                        .desc = "Clock off",
                },
-               /* WAIT + ARM power off  */
+               /* LOW POWER IDLE */
                {
                        /*
-                        * ARM gating 31us * 5 + RBC clear 65us
-                        * and some margin for SW execution, here set it
-                        * to 300us.
+                        * RBC 130us + ARM gating 93us + RBC clear 65us
+                        * + PLL2 relock 450us and some margin, here set
+                        * it to 800us.
                         */
-                       .exit_latency = 300,
-                       .target_residency = 500,
+                       .exit_latency = 800,
+                       .target_residency = 1000,
                        .enter = imx6sx_enter_wait,
                        .name = "LOW-POWER-IDLE",
                        .desc = "ARM power off",
@@ -100,16 +161,119 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
 
 int __init imx6sx_cpuidle_init(void)
 {
+       void __iomem *anatop_base = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
+       u32 val;
+#ifdef CONFIG_CPU_FREQ
+       struct imx6_cpuidle_pm_info *cpuidle_pm_info;
+       int i;
+       const u32 *mmdc_offset_array;
+       u32 wfi_code_size;
+
+       wfi_iram_base_phys = (void *)(iram_tlb_phys_addr + MX6_CPUIDLE_IRAM_ADDR_OFFSET);
+
+       /* Make sure wfi_iram_base is 8 byte aligned. */
+       if ((uintptr_t)(wfi_iram_base_phys) & (FNCPY_ALIGN - 1))
+               wfi_iram_base_phys += FNCPY_ALIGN - ((uintptr_t)wfi_iram_base_phys % (FNCPY_ALIGN));
+
+       wfi_iram_base = (void *)IMX_IO_P2V((unsigned long) wfi_iram_base_phys);
+
+       cpuidle_pm_info = wfi_iram_base;
+       cpuidle_pm_info->pbase = (phys_addr_t) wfi_iram_base_phys;
+       cpuidle_pm_info->pm_info_size = sizeof(*cpuidle_pm_info);
+       cpuidle_pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
+       cpuidle_pm_info->mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset);
+       mmdc_offset_array = imx6sx_mmdc_io_offset;
+
+       cpuidle_pm_info->mmdc_base.pbase = MX6Q_MMDC_P0_BASE_ADDR;
+       cpuidle_pm_info->mmdc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR);
+
+       cpuidle_pm_info->ccm_base.pbase = MX6Q_CCM_BASE_ADDR;
+       cpuidle_pm_info->ccm_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_CCM_BASE_ADDR);
+
+       cpuidle_pm_info->anatop_base.pbase = MX6Q_ANATOP_BASE_ADDR;
+       cpuidle_pm_info->anatop_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
+
+       cpuidle_pm_info->gpc_base.pbase = MX6Q_GPC_BASE_ADDR;
+       cpuidle_pm_info->gpc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_GPC_BASE_ADDR);
+
+       cpuidle_pm_info->iomuxc_base.pbase = MX6Q_IOMUXC_BASE_ADDR;
+       cpuidle_pm_info->iomuxc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR);
+
+       cpuidle_pm_info->l2_base.pbase = MX6Q_L2_BASE_ADDR;
+       cpuidle_pm_info->l2_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_L2_BASE_ADDR);
+
+       cpuidle_pm_info->src_base.pbase = MX6Q_SRC_BASE_ADDR;
+       cpuidle_pm_info->src_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_SRC_BASE_ADDR);
+
+       cpuidle_pm_info->sema4_base.pbase = MX6Q_SEMA4_BASE_ADDR;
+       cpuidle_pm_info->sema4_base.vbase =
+               (void __iomem *)IMX_IO_P2V(MX6Q_SEMA4_BASE_ADDR);
+
+       /* only save mmdc io offset, settings will be saved in asm code */
+       for (i = 0; i < cpuidle_pm_info->mmdc_io_num; i++)
+               cpuidle_pm_info->mmdc_io_val[i][0] = mmdc_offset_array[i];
+
+       /* code size should include cpuidle_pm_info size */
+       wfi_code_size = (&mx6sx_lpm_wfi_end -&mx6sx_lpm_wfi_start) *4 + sizeof(*cpuidle_pm_info);
+       imx6sx_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*cpuidle_pm_info),
+               &imx6sx_low_power_idle, wfi_code_size);
+#endif
+
        imx6_set_int_mem_clk_lpm(true);
-       imx6_enable_rbc(false);
-       /*
-        * set ARM power up/down timing to the fastest,
-        * sw2iso and sw can be set to one 32K cycle = 31us
-        * except for power up sw2iso which need to be
-        * larger than LDO ramp up time.
-        */
-       imx_gpc_set_arm_power_up_timing(2, 1);
-       imx_gpc_set_arm_power_down_timing(1, 1);
+
+       if (imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) {
+               /*
+                * enable RC-OSC here, as it needs at least 4ms for RC-OSC to
+                * be stable, low power idle flow can NOT endure this big
+                * latency, so we make RC-OSC self-tuning enabled here.
+                */
+               val = readl_relaxed(anatop_base + PMU_LOW_PWR_CTRL);
+               val |= 0x1;
+               writel_relaxed(val, anatop_base + PMU_LOW_PWR_CTRL);
+               /*
+                * config RC-OSC freq
+                * tune_enable = 1;tune_start = 1;hyst_plus = 0;hyst_minus = 0;
+                * osc_prog = 0xa7;
+                */
+               writel_relaxed(
+                       0x4 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT |
+                       0xa7 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT |
+                       0x1 << XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT |
+                       0x1 << XTALOSC24M_OSC_CONFIG0_START_SHIFT,
+                       anatop_base + XTALOSC24M_OSC_CONFIG0);
+               /* set count_trg = 0x2dc */
+               writel_relaxed(
+                       0x40 << XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT |
+                       0x2dc << XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT,
+                       anatop_base + XTALOSC24M_OSC_CONFIG1);
+               /* wait 4ms according to hardware design */
+               msleep(4);
+               /*
+                * now add some hysteresis, hyst_plus=3, hyst_minus=3
+                * (the minimum hysteresis that looks good is 2)
+                */
+               val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG0);
+               val &= ~((XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK <<
+                       XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
+                       (XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK <<
+                       XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT));
+               val |= (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
+                       (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT);
+               writel_relaxed(val, anatop_base  + XTALOSC24M_OSC_CONFIG0);
+               /* set the count_1m_trg = 0x2d7 */
+               val = readl_relaxed(anatop_base  + XTALOSC24M_OSC_CONFIG2);
+               val &= ~(XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK <<
+                       XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT);
+               val |= 0x2d7 << XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT;
+               writel_relaxed(val, anatop_base  + XTALOSC24M_OSC_CONFIG2);
+               /*
+                * hardware design require to write XTALOSC24M_OSC_CONFIG0 or
+                * XTALOSC24M_OSC_CONFIG1 to
+                * make XTALOSC24M_OSC_CONFIG2 write work
+                */
+               val = readl_relaxed(anatop_base  + XTALOSC24M_OSC_CONFIG1);
+               writel_relaxed(val, anatop_base  + XTALOSC24M_OSC_CONFIG1);
+       }
 
        return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
 }
diff --git a/arch/arm/mach-imx/imx6sx_low_power_idle.S b/arch/arm/mach-imx/imx6sx_low_power_idle.S
new file mode 100644 (file)
index 0000000..7ddda1c
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * Copyright (C) 2014-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>
+
+#define PM_INFO_PBASE_OFFSET                   0x0
+#define PM_INFO_RESUME_ADDR_OFFSET             0x4
+#define PM_INFO_PM_INFO_SIZE_OFFSET            0x8
+#define PM_INFO_PM_INFO_TTBR_OFFSET            0xc
+#define PM_INFO_MX6Q_MMDC_P_OFFSET             0x10
+#define PM_INFO_MX6Q_MMDC_V_OFFSET             0x14
+#define PM_INFO_MX6Q_IOMUXC_P_OFFSET           0x18
+#define PM_INFO_MX6Q_IOMUXC_V_OFFSET           0x1c
+#define PM_INFO_MX6Q_CCM_P_OFFSET              0x20
+#define PM_INFO_MX6Q_CCM_V_OFFSET              0x24
+#define PM_INFO_MX6Q_GPC_P_OFFSET              0x28
+#define PM_INFO_MX6Q_GPC_V_OFFSET              0x2c
+#define PM_INFO_MX6Q_L2_P_OFFSET               0x30
+#define PM_INFO_MX6Q_L2_V_OFFSET               0x34
+#define PM_INFO_MX6Q_ANATOP_P_OFFSET           0x38
+#define PM_INFO_MX6Q_ANATOP_V_OFFSET           0x3c
+#define PM_INFO_MX6Q_SRC_P_OFFSET              0x40
+#define PM_INFO_MX6Q_SRC_V_OFFSET              0x44
+#define PM_INFO_MX6Q_SEMA4_P_OFFSET            0x48
+#define PM_INFO_MX6Q_SEMA4_V_OFFSET            0x4c
+#define PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET   0x50
+#define PM_INFO_MMDC_IO_NUM_OFFSET             0x54
+#define PM_INFO_MMDC_IO_VAL_OFFSET             0x58
+
+#define        MX6Q_MMDC_MAPSR         0x404
+#define MX6Q_MMDC_MPDGCTRL0    0x83c
+#define MX6Q_SRC_GPR1  0x20
+#define MX6Q_SRC_GPR2  0x24
+#define MX6Q_GPC_IMR1  0x08
+#define MX6Q_GPC_IMR2  0x0c
+#define MX6Q_GPC_IMR3  0x10
+#define MX6Q_GPC_IMR4  0x14
+#define MX6Q_CCM_CCR   0x0
+
+.globl mx6sx_lpm_wfi_start
+.globl mx6sx_lpm_wfi_end
+
+       .macro  pll_do_wait_lock
+1:
+       ldr     r7, [r10, r8]
+       ands    r7, #0x80000000
+       beq     1b
+
+       .endm
+
+       .macro  ccm_do_wait
+2:
+       ldr     r7, [r10, #0x48]
+       cmp     r7, #0x0
+       bne     2b
+
+       .endm
+
+       .macro  ccm_enter_idle
+
+       ldr     r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+
+       /* set ahb to 3MHz */
+       ldr     r7, [r10, #0x14]
+       orr     r7, r7, #0x1c00
+       str     r7, [r10, #0x14]
+
+       /* set perclk to 6MHz */
+       ldr     r7, [r10, #0x1c]
+       bic     r7, r7, #0x3f
+       orr     r7, r7, #0x3
+       str     r7, [r10, #0x1c]
+
+       /* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */
+       ldr     r7, [r10, #0x14]
+       orr     r7, r7, #0x2
+       orr     r7, r7, #(0x7 << 3)
+       str     r7, [r10, #0x14]
+
+       ccm_do_wait
+
+       ldr     r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+
+       /* set pll1_sw to from pll1 main */
+       ldr     r7, [r10, #0xc]
+       bic     r7, r7, #0x4
+       str     r7, [r10, #0xc]
+
+       /* set step from osc */
+       ldr     r7, [r10, #0xc]
+       bic     r7, r7, #0x100
+       str     r7, [r10, #0xc]
+
+       /* set pll1_sw to from step */
+       ldr     r7, [r10, #0xc]
+       orr     r7, r7, #0x4
+       str     r7, [r10, #0xc]
+
+       ldr     r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
+
+       /* Disable PLL1 bypass output */
+       ldr     r7, [r10]
+       bic     r7, r7, #0x12000
+       str     r7, [r10]
+
+       /*
+        * disable pll2, suppose when system enter low
+        * power idle mode, only 396MHz pfd needs pll2,
+        * now we switch arm clock to OSC, we can disable
+        * pll2 now, gate pll2_pfd2 first.
+        */
+       ldr     r7, [r10, #0x100]
+       orr     r7, #0x800000
+       str     r7, [r10, #0x100]
+
+       ldr     r7, [r10, #0x30]
+       orr     r7, r7, #0x1000
+       bic     r7, r7, #0x2000
+       str     r7, [r10, #0x30]
+
+       .endm
+
+       .macro  ccm_exit_idle
+
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
+
+       /* enable pll2 and pll2_pfd2 */
+       ldr     r7, [r10, #0x30]
+       bic     r7, r7, #0x1000
+       orr     r7, r7, #0x2000
+       str     r7, [r10, #0x30]
+
+       ldr     r8, =0x30
+       pll_do_wait_lock
+
+       ldr     r7, [r10, #0x100]
+       bic     r7, #0x800000
+       str     r7, [r10, #0x100]
+
+       /* enable PLL1 bypass output */
+       ldr     r7, [r10]
+       orr     r7, r7, #0x12000
+       str     r7, [r10]
+
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET]
+
+       /* set perclk back to 24MHz */
+       ldr     r7, [r10, #0x1c]
+       bic     r7, r7, #0x3f
+       str     r7, [r10, #0x1c]
+
+       /* set mmdc back to 24MHz */
+       ldr     r7, [r10, #0x14]
+       bic     r7, r7, #0x7
+       bic     r7, r7, #(0x7 << 3)
+       str     r7, [r10, #0x14]
+
+       /* set ahb div back to 24MHz */
+       ldr     r7, [r10, #0x14]
+       bic     r7, r7, #0x1c00
+       str     r7, [r10, #0x14]
+
+       ccm_do_wait
+
+       /* set pll1_sw to from pll1 main */
+       ldr     r7, [r10, #0xc]
+       bic     r7, r7, #0x4
+       str     r7, [r10, #0xc]
+
+       /* set step from pll2_pfd2 */
+       ldr     r7, [r10, #0xc]
+       orr     r7, r7, #0x100
+       str     r7, [r10, #0xc]
+
+       /* set pll1_sw to from step */
+       ldr     r7, [r10, #0xc]
+       orr     r7, r7, #0x4
+       str     r7, [r10, #0xc]
+
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
+
+       .endm
+
+       .macro  anatop_enter_idle
+
+       ldr     r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
+
+       /*
+        * check whether any PLL is enabled, as only when
+        * there is no PLLs enabled, 2P5 and 1P1 can be
+        * off and only enable weak ones.
+        */
+
+       /* arm pll1 */
+       ldr     r7, [r10, #0]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* sys pll2 */
+       ldr     r7, [r10, #0x30]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* usb pll3 */
+       ldr     r7, [r10, #0x10]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* audio pll4 */
+       ldr     r7, [r10, #0x70]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* vidio pll5 */
+       ldr     r7, [r10, #0xa0]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* enet pll6 */
+       ldr     r7, [r10, #0xe0]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* usb host pll7 */
+       ldr     r7, [r10, #0x20]
+       ands    r7, r7, #(1 << 31)
+       bne     10f
+
+       /* enable weak 2P5 and turn off regular 2P5 */
+       ldr     r7, [r10, #0x130]
+       orr     r7, r7, #0x40000
+       str     r7, [r10, #0x130]
+       bic     r7, r7, #0x1
+       str     r7, [r10, #0x130]
+
+       /* enable weak 1p1 and turn off regular 1P1 */
+       ldr     r7, [r10, #0x110]
+       orr     r7, r7, #0x40000
+       str     r7, [r10, #0x110]
+       bic     r7, r7, #0x1
+       str     r7, [r10, #0x110]
+
+       /* check whether ARM LDO is bypassed */
+       ldr     r7, [r10, #0x140]
+       and     r7, r7, #0x1f
+       cmp     r7, #0x1f
+       bne     10f
+
+       /* low power band gap enable */
+       ldr     r7, [r10, #0x270]
+       orr     r7, r7, #0x20
+       str     r7, [r10, #0x270]
+
+       /* turn off the bias current from the regular bandgap */
+       ldr     r7, [r10, #0x270]
+       orr     r7, r7, #0x80
+       str     r7, [r10, #0x270]
+
+       /*
+        * clear the REFTOP_SELFBIASOFF,
+        * self-bias circuit of the band gap.
+        * Per RM, should be cleared when
+        * band gap is powered down.
+        */
+       ldr     r7, [r10, #0x150]
+       bic     r7, r7, #0x8
+       str     r7, [r10, #0x150]
+
+       /* turn off regular bandgap */
+       ldr     r7, [r10, #0x150]
+       orr     r7, r7, #0x1
+       str     r7, [r10, #0x150]
+
+       /* only switch to RC-OSC clk after TO1.2 */
+       ldr     r7, [r10, #0x260]
+       and     r7, r7, #0x3
+       cmp     r7, #0x2
+       blt     10f
+
+       /* switch to RC-OSC */
+       ldr     r7, [r10, #0x270]
+       orr     r7, r7, #0x10
+       str     r7, [r10, #0x270]
+
+       /* turn off XTAL-OSC */
+       ldr     r7, [r10, #0x150]
+       orr     r7, r7, #0x40000000
+       str     r7, [r10, #0x150]
+10:
+       /* lower OSC current by 37.5% */
+       ldr     r7, [r10, #0x150]
+       orr     r7, r7, #0x6000
+       str     r7, [r10, #0x150]
+
+       .endm
+
+       .macro anatop_exit_idle
+
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
+
+       /* increase OSC current to normal */
+       ldr     r7, [r10, #0x150]
+       bic     r7, r7, #0x6000
+       str     r7, [r10, #0x150]
+
+       /* only switch to RC-OSC after TO1.2 */
+       ldr     r7, [r10, #0x260]
+       and     r7, r7, #0x3
+       cmp     r7, #0x2
+       blt     15f
+
+       /* turn on XTAL-OSC and detector */
+       ldr     r7, [r10, #0x150]
+       bic     r7, r7, #0x40000000
+       orr     r7, r7, #0x10000
+       str     r7, [r10, #0x150]
+
+       /* wait for XTAL stable */
+14:
+       ldr     r7, [r10, #0x150]
+       ands    r7, r7, #0x8000
+       beq     14b
+
+       /* switch to XTAL-OSC */
+       ldr     r7, [r10, #0x270]
+       bic     r7, r7, #0x10
+       str     r7, [r10, #0x270]
+
+       /* turn off XTAL-OSC detector */
+       ldr     r7, [r10, #0x150]
+       bic     r7, r7, #0x10000
+       str     r7, [r10, #0x150]
+15:
+       /* check whether we need to enable 2P5/1P1 */
+       ldr     r7, [r10, #0x110]
+       ands    r7, r7, #0x40000
+       beq     11f
+
+       /* check whether ARM LDO is bypassed */
+       ldr     r7, [r10, #0x140]
+       and     r7, r7, #0x1f
+       cmp     r7, #0x1f
+       bne     12f
+
+       /* turn on regular bandgap and wait for stable */
+       ldr     r7, [r10, #0x150]
+       bic     r7, r7, #0x1
+       str     r7, [r10, #0x150]
+13:
+       ldr     r7, [r10, #0x150]
+       ands    r7, #0x80
+       beq     13b
+
+       /*
+        * set the REFTOP_SELFBIASOFF,
+        * self-bias circuit of the band gap.
+        */
+       ldr     r7, [r10, #0x150]
+       orr     r7, r7, #0x8
+       str     r7, [r10, #0x150]
+
+       /* turn on the bias current from the regular bandgap */
+       ldr     r7, [r10, #0x270]
+       bic     r7, r7, #0x80
+       str     r7, [r10, #0x270]
+
+       /* low power band gap disable */
+       ldr     r7, [r10, #0x270]
+       bic     r7, r7, #0x20
+       str     r7, [r10, #0x270]
+12:
+       /* enable regular 2P5 and turn off weak 2P5 */
+       ldr     r7, [r10, #0x130]
+       orr     r7, r7, #0x1
+       str     r7, [r10, #0x130]
+
+       /* Ensure the 2P5 is up. */
+3:
+       ldr     r7, [r10, #0x130]
+       ands    r7, r7, #0x20000
+       beq     3b
+       ldr     r7, [r10, #0x130]
+       bic     r7, r7, #0x40000
+       str     r7, [r10, #0x130]
+
+       /* enable regular 1p1 and turn off weak 1P1 */
+       ldr     r7, [r10, #0x110]
+       orr     r7, r7, #0x1
+       str     r7, [r10, #0x110]
+4:
+       ldr     r7, [r10, #0x110]
+       ands    r7, r7, #0x20000
+       beq     4b
+       ldr     r7, [r10, #0x110]
+       bic     r7, r7, #0x40000
+       str     r7, [r10, #0x110]
+11:
+       .endm
+
+       .macro  disable_l1_dcache
+
+       /*
+        * Flush all data from the L1 data cache before disabling
+        * SCTLR.C bit.
+        */
+       push    {r0 - r10, lr}
+       ldr     r7, =v7_flush_dcache_all
+       mov     lr, pc
+       mov     pc, r7
+       pop     {r0 - r10, lr}
+
+       /* disable d-cache */
+       mrc     p15, 0, r7, c1, c0, 0
+       bic     r7, r7, #(1 << 2)
+       mcr     p15, 0, r7, c1, c0, 0
+       dsb
+       isb
+
+       push    {r0 - r10, lr}
+       ldr     r7, =v7_flush_dcache_all
+       mov     lr, pc
+       mov     pc, r7
+       pop     {r0 - r10, lr}
+
+       .endm
+
+       .macro mmdc_enter_dvfs_mode
+
+       /* disable automatic power savings. */
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       orr     r7, r7, #0x1
+       str     r7, [r10, #MX6Q_MMDC_MAPSR]
+
+       /* disable power down timer */
+       ldr     r7, [r10, #0x4]
+       bic     r7, r7, #0xff00
+       str     r7, [r10, #0x4]
+
+       /* make the DDR explicitly enter self-refresh. */
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       orr     r7, r7, #(1 << 21)
+       str     r7, [r10, #MX6Q_MMDC_MAPSR]
+5:
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       ands    r7, r7, #(1 << 25)
+       beq     5b
+
+       .endm
+
+       .macro  resume_mmdc
+
+       /* restore MMDC IO */
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
+
+       ldr     r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+       ldr     r7, =PM_INFO_MMDC_IO_VAL_OFFSET
+       add     r7, r7, r0
+6:
+       ldr     r8, [r7], #0x4
+       ldr     r9, [r7], #0x4
+       str     r9, [r10, r8]
+       subs    r6, r6, #0x1
+       bne     6b
+
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
+
+       /* reset read FIFO, RST_RD_FIFO */
+       ldr     r7, =MX6Q_MMDC_MPDGCTRL0
+       ldr     r6, [r10, r7]
+       orr     r6, r6, #(1 << 31)
+       str     r6, [r10, r7]
+7:
+       ldr     r6, [r10, r7]
+       ands    r6, r6, #(1 << 31)
+       bne     7b
+
+       /* reset FIFO a second time */
+       ldr     r6, [r10, r7]
+       orr     r6, r6, #(1 << 31)
+       str     r6, [r10, r7]
+8:
+       ldr     r6, [r10, r7]
+       ands    r6, r6, #(1 << 31)
+       bne     8b
+
+       /* let DDR out of self-refresh */
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       bic     r7, r7, #(1 << 21)
+       str     r7, [r10, #MX6Q_MMDC_MAPSR]
+9:
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       ands    r7, r7, #(1 << 25)
+       bne     9b
+
+       /* enable power down timer */
+       ldr     r7, [r10, #0x4]
+       orr     r7, r7, #0x5500
+       str     r7, [r10, #0x4]
+
+       /* enable DDR auto power saving */
+       ldr     r7, [r10, #MX6Q_MMDC_MAPSR]
+       bic     r7, r7, #0x1
+       str     r7, [r10, #MX6Q_MMDC_MAPSR]
+
+       .endm
+
+       .macro  sema4_lock
+
+       /* lock share memory sema4 */
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET]
+       ldrb    r6, =0x1
+16:
+       ldrb    r7, [r10, #0x6]
+       cmp     r7, #0x0
+       bne     16b
+       strb    r6, [r10, #0x6]
+
+       .endm
+
+       .macro  sema4_unlock
+
+       /* unlock share memory sema4 */
+       cmp     r5, #0x0
+       ldreq   r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET]
+       ldrne   r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET]
+       ldrb    r6, =0x0
+       strb    r6, [r10, #0x6]
+
+       .endm
+
+       .macro  tlb_set_to_ocram
+
+       /* save ttbr */
+       mrc     p15, 0, r7, c2, c0, 1
+       str     r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
+
+       /*
+        * 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]
+
+       /* Flush the BTAC. */
+       ldr     r6, =0x0
+       mcr     p15, 0, r6, c7, c1, 6
+
+       /* 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
+
+       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
+
+       .endm
+
+       .macro  tlb_back_to_ddr
+
+       /* 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
+
+       /* restore ttbr */
+       ldr     r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
+       mcr     p15, 0, r7, c2, c0, 1
+
+       .endm
+
+.extern iram_tlb_phys_addr
+
+/* imx6sx_low_power_idle */
+
+       .align 3
+ENTRY(imx6sx_low_power_idle)
+mx6sx_lpm_wfi_start:
+       push    {r4 - r10}
+
+       /* get necessary info from pm_info */
+       ldr     r1, [r0, #PM_INFO_PBASE_OFFSET]
+       ldr     r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
+
+       /*
+        * counting the resume address in iram
+        * to set it in SRC register.
+        */
+       ldr     r5, =imx6sx_low_power_idle
+       ldr     r6, =wakeup
+       sub     r6, r6, r5
+       add     r8, r1, r2
+       add     r3, r8, r6
+
+       /* store physical resume addr and pm_info address. */
+       ldr     r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
+       str     r3, [r10, #0x20]
+       str     r1, [r10, #0x24]
+
+       /* save disagnostic register */
+       mrc     p15, 0, r7, c15, c0, 1
+       str     r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
+
+       /* set ARM power to be gated */
+       ldr     r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+       ldr     r7, =0x1
+       str     r7, [r10, #0x2a0]
+
+       disable_l1_dcache
+
+#ifdef CONFIG_CACHE_L2X0
+       /* sync L2 */
+       ldr     r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
+
+       /* Wait for background operations to complete. */
+wait_for_l2_to_idle:
+       ldr     r7, [r10, #0x730]
+       cmp     r7, #0x0
+       bne     wait_for_l2_to_idle
+
+       mov     r7, #0x0
+       str     r7, [r10, #0x730]
+       /* disable L2 */
+       str     r7, [r10, #0x100]
+
+       dsb
+       isb
+#endif
+
+       tlb_set_to_ocram
+
+       /* make sure MMDC in self-refresh */
+       ldr     r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+       mmdc_enter_dvfs_mode
+
+       /* save DDR IO settings */
+       ldr     r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+       ldr     r6, =0x0
+       ldr     r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+       ldr     r8, =PM_INFO_MMDC_IO_VAL_OFFSET
+       add     r8, r8, r0
+save_and_set_mmdc_io_lpm:
+       ldr     r9, [r8], #0x4
+       ldr     r5, [r10, r9]
+       str     r6, [r10, r9]
+       str     r5, [r8], #0x4
+       subs    r7, r7, #0x1
+       bne     save_and_set_mmdc_io_lpm
+
+       mov     r5, #0x0
+       sema4_lock
+       ccm_enter_idle
+       anatop_enter_idle
+       sema4_unlock
+
+       /*
+        * mask all GPC interrupts before
+        * enabling the RBC counters to
+        * avoid the counter starting too
+        * early if an interupt is already
+        * pending.
+        */
+       ldr     r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+       ldr     r4, [r10, #MX6Q_GPC_IMR1]
+       ldr     r5, [r10, #MX6Q_GPC_IMR2]
+       ldr     r6, [r10, #MX6Q_GPC_IMR3]
+       ldr     r7, [r10, #MX6Q_GPC_IMR4]
+
+       ldr     r3, =0xffffffff
+       str     r3, [r10, #MX6Q_GPC_IMR1]
+       str     r3, [r10, #MX6Q_GPC_IMR2]
+       str     r3, [r10, #MX6Q_GPC_IMR3]
+       str     r3, [r10, #MX6Q_GPC_IMR4]
+
+       /*
+        * enable the RBC bypass counter here
+        * to hold off the interrupts. RBC counter
+        * = 4 (120us). With this setting, the latency
+        * from wakeup interrupt to ARM power up
+        * is ~130uS.
+        */
+       ldr     r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+       ldr     r3, [r10, #MX6Q_CCM_CCR]
+       bic     r3, r3, #(0x3f << 21)
+       orr     r3, r3, #(0x4 << 21)
+       str     r3, [r10, #MX6Q_CCM_CCR]
+
+       /* enable the counter. */
+       ldr     r3, [r10, #MX6Q_CCM_CCR]
+       orr     r3, r3, #(0x1 << 27)
+       str     r3, [r10, #MX6Q_CCM_CCR]
+
+       /* unmask all the GPC interrupts. */
+       ldr     r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+       str     r4, [r10, #MX6Q_GPC_IMR1]
+       str     r5, [r10, #MX6Q_GPC_IMR2]
+       str     r6, [r10, #MX6Q_GPC_IMR3]
+       str     r7, [r10, #MX6Q_GPC_IMR4]
+
+       /*
+        * now delay for a short while (3usec)
+        * ARM is at 24MHz at this point
+        * so a short loop should be enough.
+        * this delay is required to ensure that
+        * the RBC counter can start counting in
+        * case an interrupt is already pending
+        * or in case an interrupt arrives just
+        * as ARM is about to assert DSM_request.
+        */
+       ldr     r4, =50
+rbc_loop:
+       subs    r4, r4, #0x1
+       bne     rbc_loop
+
+       wfi
+
+       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
+
+       mov     r5, #0x0
+       sema4_lock
+       anatop_exit_idle
+       ccm_exit_idle
+       sema4_unlock
+       resume_mmdc
+
+       /* clear ARM power gate setting */
+       ldr     r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+       ldr     r7, =0x0
+       str     r7, [r10, #0x2a0]
+
+       /* enable d-cache */
+       mrc     p15, 0, r7, c1, c0, 0
+       orr     r7, r7, #(1 << 2)
+       mcr     p15, 0, r7, c1, c0, 0
+
+#ifdef CONFIG_CACHE_L2X0
+       ldr     r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
+       mov     r7, #0x1
+       /* enable L2 */
+       str     r7, [r10, #0x100]
+#endif
+
+       tlb_back_to_ddr
+
+       /* Restore registers */
+       pop     {r4 - r10}
+       mov     pc, lr
+
+wakeup:
+
+       /* invalidate L1 I-cache first */
+       mov     r1, #0x0
+       mcr     p15, 0, r1, c7, c5, 0
+       mcr     p15, 0, r1, c7, c5, 0
+       mcr     p15, 0, r1, c7, c5, 6
+       /* enable the Icache and branch prediction */
+       mov     r1, #0x1800
+       mcr     p15, 0, r1, c1, c0, 0
+       isb
+       /* restore disagnostic register */
+       ldr     r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
+       mcr     p15, 0, r7, c15, c0, 1
+
+       /* get physical resume address from pm_info. */
+       ldr     lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+       /* clear core0's entry and parameter */
+       ldr     r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
+       mov     r7, #0x0
+       str     r7, [r10, #MX6Q_SRC_GPR1]
+       str     r7, [r10, #MX6Q_SRC_GPR2]
+
+       /* clear ARM power gate setting */
+       ldr     r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
+       ldr     r7, =0x0
+       str     r7, [r10, #0x2a0]
+
+       mov     r5, #0x1
+       sema4_lock
+       anatop_exit_idle
+       ccm_exit_idle
+       sema4_unlock
+       resume_mmdc
+
+       /* Restore registers */
+       mov     pc, lr
+       .ltorg
+mx6sx_lpm_wfi_end:
index faf4da1..faf37e0 100644 (file)
@@ -102,6 +102,8 @@ static void __init imx6sx_init_late(void)
 {
        if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
                platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
+
+       imx6sx_cpuidle_init();
 }
 
 static void __init imx6sx_map_io(void)
index 890b292..e045ca9 100644 (file)
@@ -503,6 +503,8 @@ static void __init imx6sx_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);
 
+       clk_set_parent(clks[IMX6SX_CLK_PERCLK_SEL], clks[IMX6SX_CLK_OSC]);
+
        for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
                clk_prepare_enable(clks[clks_init_on[i]]);