MLK-12262-4 ARM: imx: fix low power idle issue on i.MX7D TO1.1
authorAnson Huang <Anson.Huang@nxp.com>
Tue, 26 Jan 2016 07:41:01 +0000 (15:41 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:49:58 +0000 (14:49 -0500)
For low power idle with ARM power gated, per hardware requirement,
there must be no interrupt coming during the power down
process of ARM core, so RBC counter is enabled to hold interrupts
and GIC must be disabled at the moment;

The hardware design team recommends ~240us is required during ARM
core power down, so we update the RBC counter value to 8(~240us);

Update GPC SCU and CPU power up/down timing according to design
team's recommendation.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
arch/arm/mach-imx/common.h
arch/arm/mach-imx/cpuidle-imx7d.c
arch/arm/mach-imx/gpcv2.c
arch/arm/mach-imx/imx7d_low_power_idle.S
arch/arm/mach-imx/mx7.h
arch/arm/mach-imx/pm-imx7.c

index 5795ff6..ab22a4e 100644 (file)
@@ -135,6 +135,7 @@ static inline void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn) {}
 void __init imx_gpcv2_check_dt(void);
 void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode mode);
 void imx_gpcv2_set_cpu_power_gate_in_idle(bool pdn);
+void imx_gpcv2_enable_rbc(bool enable);
 unsigned long save_ttbr1(void);
 void restore_ttbr1(unsigned long ttbr1);
 void imx_gpc_mask_all(void);
index 28ad5b0..caa637a 100644 (file)
@@ -74,6 +74,8 @@ struct imx7_cpuidle_pm_info {
        struct imx7_pm_base anatop_base;
        struct imx7_pm_base src_base;
        struct imx7_pm_base iomuxc_gpr_base;
+       struct imx7_pm_base gpc_base;
+       struct imx7_pm_base gic_dist_base;
 } __aligned(8);
 
 static atomic_t master_lpi = ATOMIC_INIT(0);
@@ -146,8 +148,8 @@ static struct cpuidle_driver imx7d_cpuidle_driver = {
                },
                /* LOW POWER IDLE */
                {
-                       .exit_latency = 150,
-                       .target_residency = 300,
+                       .exit_latency = 500,
+                       .target_residency = 1000,
                        .flags = CPUIDLE_FLAG_TIMER_STOP,
                        .enter = imx7d_enter_low_power_idle,
                        .name = "LOW-POWER-IDLE",
@@ -280,6 +282,14 @@ int __init imx7d_cpuidle_init(void)
        cpuidle_pm_info->iomuxc_gpr_base.vbase =
                (void __iomem *)IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR);
 
+       cpuidle_pm_info->gpc_base.pbase = MX7D_GPC_BASE_ADDR;
+       cpuidle_pm_info->gpc_base.vbase =
+               (void __iomem *)IMX_IO_P2V(MX7D_GPC_BASE_ADDR);
+
+       cpuidle_pm_info->gic_dist_base.pbase = MX7D_GIC_BASE_ADDR;
+       cpuidle_pm_info->gic_dist_base.vbase =
+               (void __iomem *)IMX_IO_P2V(MX7D_GIC_BASE_ADDR);
+
        imx7d_enable_rcosc();
 
 #ifdef CONFIG_HOTPLUG_CPU
index 83c1a30..08bbbb7 100644 (file)
@@ -65,6 +65,7 @@
 #define BM_LPCR_M4_MASK_DSM_TRIGGER            0x80000000
 #define BM_SLPCR_EN_DSM                                0x80000000
 #define BM_SLPCR_RBC_EN                                0x40000000
+#define BM_SLPCR_REG_BYPASS_COUNT              0x3f000000
 #define BM_SLPCR_VSTBY                         0x4
 #define BM_SLPCR_SBYOS                         0x2
 #define BM_SLPCR_BYPASS_PMIC_READY             0x1
@@ -410,6 +411,7 @@ void imx_gpcv2_set_cpu_power_gate_in_idle(bool pdn)
                writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
                        BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK,
                        gpc_base + GPC_PGC_ACK_SEL_A7);
+               imx_gpcv2_enable_rbc(false);
        }
        spin_unlock_irqrestore(&gpcv2_lock, flags);
 }
@@ -463,6 +465,40 @@ int imx_gpcv2_mf_power_on(unsigned int irq, unsigned int on)
        return 0;
 }
 
+void imx_gpcv2_enable_rbc(bool enable)
+{
+       u32 val;
+
+       /*
+        * need to mask all interrupts in GPC before
+        * operating RBC configurations
+        */
+       imx_gpcv2_mask_all();
+
+       /* configure RBC enable bit */
+       val = readl_relaxed(gpc_base + GPC_SLPCR);
+       val &= ~BM_SLPCR_RBC_EN;
+       val |= enable ? BM_SLPCR_RBC_EN : 0;
+       writel_relaxed(val, gpc_base + GPC_SLPCR);
+
+       /* configure RBC count */
+       val = readl_relaxed(gpc_base + GPC_SLPCR);
+       val &= ~BM_SLPCR_REG_BYPASS_COUNT;
+       val |= enable ? BM_SLPCR_REG_BYPASS_COUNT : 0;
+       writel(val, gpc_base + GPC_SLPCR);
+
+       /*
+        * need to delay at least 2 cycles of CKIL(32K)
+        * due to hardware design requirement, which is
+        * ~61us, here we use 65us for safe
+        */
+       udelay(65);
+
+       /* restore GPC interrupt mask settings */
+       imx_gpcv2_restore_all();
+}
+
+
 void imx_gpcv2_pre_suspend(bool arm_power_off)
 {
        void __iomem *reg_imr1 = gpc_base + GPC_IMR1_CORE0;
@@ -548,7 +584,7 @@ void imx_gpcv2_post_resume(void)
        /* set mega/fast mix in A7 domain */
        writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_MAPPING);
        /* set SCU timing */
-       writel_relaxed((0x59 << 10) | 0x5B | (0x51 << 20),
+       writel_relaxed((0x59 << 10) | 0x5B | (0x2 << 20),
                gpc_base + GPC_PGC_SCU_TIMING);
 
        /* set C0/C1 power up timming per design requirement */
@@ -559,7 +595,7 @@ void imx_gpcv2_post_resume(void)
 
        val = readl_relaxed(gpc_base + GPC_PGC_C1_PUPSCR);
        val &= ~BM_GPC_PGC_CORE_PUPSCR;
-       val |= (0x19 << 7);
+       val |= (0x1A << 7);
        writel_relaxed(val, gpc_base + GPC_PGC_C1_PUPSCR);
 
        val = readl_relaxed(gpc_base + GPC_SLPCR);
@@ -593,6 +629,9 @@ void imx_gpcv2_post_resume(void)
        writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
                BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK,
                gpc_base + GPC_PGC_ACK_SEL_A7);
+
+       /* disable RBC */
+       imx_gpcv2_enable_rbc(false);
 }
 
 static struct irq_chip imx_gpcv2_chip = {
@@ -796,7 +835,7 @@ static int __init imx_gpcv2_init(struct device_node *node,
        /* set mega/fast mix in A7 domain */
        writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_MAPPING);
        /* set SCU timing */
-       writel_relaxed((0x59 << 10) | 0x5B | (0x51 << 20),
+       writel_relaxed((0x59 << 10) | 0x5B | (0x2 << 20),
                gpc_base + GPC_PGC_SCU_TIMING);
 
        /* set C0/C1 power up timming per design requirement */
@@ -807,7 +846,7 @@ static int __init imx_gpcv2_init(struct device_node *node,
 
        val = readl_relaxed(gpc_base + GPC_PGC_C1_PUPSCR);
        val &= ~BM_GPC_PGC_CORE_PUPSCR;
-       val |= (0x19 << 7);
+       val |= (0x1A << 7);
        writel_relaxed(val, gpc_base + GPC_PGC_C1_PUPSCR);
 
        writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
@@ -827,6 +866,9 @@ static int __init imx_gpcv2_init(struct device_node *node,
        val |= BM_GPC_MLPCR_MEMLP_CTL_DIS;
        writel_relaxed(val, gpc_base + GPC_MLPCR);
 
+       /* disable RBC */
+       imx_gpcv2_enable_rbc(false);
+
        return 0;
 }
 
index 07460dd..38b6541 100644 (file)
 #define PM_INFO_MX7D_SRC_V_OFFSET              0x48
 #define PM_INFO_MX7D_IOMUXC_GPR_P_OFFSET       0x4c
 #define PM_INFO_MX7D_IOMUXC_GPR_V_OFFSET       0x50
+#define PM_INFO_MX7D_GPC_P_OFFSET              0x54
+#define PM_INFO_MX7D_GPC_V_OFFSET              0x58
+#define PM_INFO_MX7D_GIC_DIST_P_OFFSET         0x5c
+#define PM_INFO_MX7D_GIC_DIST_V_OFFSET         0x60
 
 #define MX7D_SRC_GPR1  0x74
 #define MX7D_SRC_GPR2  0x78
 #define MX7D_SRC_GPR3  0x7c
 #define MX7D_SRC_GPR4  0x80
+#define MX7D_GPC_IMR1  0x30
+#define MX7D_GPC_IMR2  0x34
+#define MX7D_GPC_IMR3  0x38
+#define MX7D_GPC_IMR4  0x3c
 #define DDRC_STAT      0x4
 #define DDRC_PWRCTL    0x30
 #define DDRC_DBG1      0x304
@@ -617,6 +625,61 @@ last_cpu:
        ddrc_enter_self_refresh
        ccm_enter_idle
        anatop_enter_idle
+
+       ldr     r10, [r0, #PM_INFO_MX7D_GIC_DIST_V_OFFSET]
+       ldr     r7, =0x0
+       str     r7, [r10]
+
+       ldr     r10, [r0, #PM_INFO_MX7D_GPC_V_OFFSET]
+       ldr     r4, [r10, #MX7D_GPC_IMR1]
+       ldr     r5, [r10, #MX7D_GPC_IMR2]
+       ldr     r6, [r10, #MX7D_GPC_IMR3]
+       ldr     r7, [r10, #MX7D_GPC_IMR4]
+
+       ldr     r8, =0xffffffff
+       str     r8, [r10, #MX7D_GPC_IMR1]
+       str     r8, [r10, #MX7D_GPC_IMR2]
+       str     r8, [r10, #MX7D_GPC_IMR3]
+       str     r8, [r10, #MX7D_GPC_IMR4]
+
+       /*
+        * enable the RBC bypass counter here
+        * to hold off the interrupts. RBC counter
+        * = 8 (240us). With this setting, the latency
+        * from wakeup interrupt to ARM power up
+        * is ~250uS.
+        */
+       ldr     r8, [r10, #0x14]
+       bic     r8, r8, #(0x3f << 24)
+       orr     r8, r8, #(0x8 << 24)
+       str     r8, [r10, #0x14]
+
+       /* enable the counter. */
+       ldr     r8, [r10, #0x14]
+       orr     r8, r8, #(0x1 << 30)
+       str     r8, [r10, #0x14]
+
+       /* unmask all the GPC interrupts. */
+       str     r4, [r10, #MX7D_GPC_IMR1]
+       str     r5, [r10, #MX7D_GPC_IMR2]
+       str     r6, [r10, #MX7D_GPC_IMR3]
+       str     r7, [r10, #MX7D_GPC_IMR4]
+
+       /*
+        * now delay for a short while (30usec)
+        * 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, =5
+rbc_loop:
+       subs    r4, r4, #0x1
+       bne     rbc_loop
+
        /* set low power idle enter flag */
        ldr     r7, =0x1
        str     r7, [r0, #PM_INFO_PM_INFO_LPI_ENTER_OFFSET]
@@ -679,6 +742,10 @@ do_exit_wfi:
        anatop_exit_idle
        ccm_exit_idle
        ddrc_exit_self_refresh
+
+       ldr     r10, [r0, #PM_INFO_MX7D_GIC_DIST_V_OFFSET]
+       ldr     r7, =0x1
+       str     r7, [r10]
        /* clear lpi enter flag */
        ldr     r7, =0x0
        str     r7, [r0, #PM_INFO_PM_INFO_LPI_ENTER_OFFSET]
index 979a1c6..afbeaef 100644 (file)
@@ -40,6 +40,8 @@
 #define MX7D_AIPS2_SIZE                 0x400000
 #define MX7D_AIPS3_BASE_ADDR            0x30900000
 #define MX7D_AIPS3_SIZE                 0x300000
+#define MX7D_GIC_BASE_ADDR              0x31000000
+#define MX7D_GIC_SIZE                   0x100000
 
 #define TT_ATTRIB_NON_CACHEABLE_1M     0x802
 #define MX7_IRAM_TLB_SIZE              0x4000
index 2b56f07..6e40d40 100644 (file)
@@ -895,6 +895,14 @@ void __init imx7_pm_map_io(void)
                        ((MX7D_AIPS3_BASE_ADDR + i * 0x100000) & 0xFFF00000) |
                        TT_ATTRIB_NON_CACHEABLE_1M;
        }
+
+       /*
+        * Make sure the GIC virtual address has a mapping in the
+        * IRAM page table.
+        */
+       j = ((IMX_IO_P2V(MX7D_GIC_BASE_ADDR) >> 20) << 2) / 4;
+       *((unsigned long *)iram_tlb_base_addr + j) =
+               (MX7D_GIC_BASE_ADDR & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M;
 }
 
 static int __init imx7_suspend_init(const struct imx7_pm_socdata *socdata)