aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2013-10-16 07:52:00 -0400
committerShawn Guo <shawn.guo@linaro.org>2013-10-20 21:39:26 -0400
commitd48866fefdac239a4e02777e712aad60db9ee8a8 (patch)
treebe0bb666ba9a8d0c29a262407e885ee91abb85e7
parent1d674a73c59211cc33cb9c2954659033d8458fa9 (diff)
ARM: imx: ensure dsm_request signal is not asserted when setting LPM
There is a defect in imx6 LPM design. When SW tries to enter low power mode with following sequence, the chip will enter low power mode before A9 CPU execute WFI instruction: 1. Set CCM_CLPCR[1:0] to 2'b00; 2. ARM CPU enters WFI; 3. ARM CPU wakeup from an interrupt event, which is masked by GPC or not visible to GPC, such as interrupt from local timer; 4. Set CCM_CLPCR[1:0] to 2'b01 or 2'b10; 5. ARM CPU execute WFI. Before the last step, the chip will enter WAIT mode if CCM_CLPCR[1:0] is set to 2'b01, or enter STOP mode if CCM_CLPCR[1:0] is set to 2'b10. The patch implements a recommended workaround for this issue. 1. SW triggers irq #32(IOMUX) to be always pending manually by setting IOMUX_GPR1_GINT bit; 2. SW should then unmask it in GPC before setting CCM LPM; 3. SW should mask it right after CCM LPM is set (bit0-1 of CCM_CLPCR). Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
-rw-r--r--arch/arm/mach-imx/common.h3
-rw-r--r--arch/arm/mach-imx/gpc.c4
-rw-r--r--arch/arm/mach-imx/pm-imx6q.c26
3 files changed, 31 insertions, 2 deletions
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index fde6661ef70d..7cbe22d0c6e9 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -13,6 +13,7 @@
13 13
14#include <linux/reboot.h> 14#include <linux/reboot.h>
15 15
16struct irq_data;
16struct platform_device; 17struct platform_device;
17struct pt_regs; 18struct pt_regs;
18struct clk; 19struct clk;
@@ -136,6 +137,8 @@ void imx_gpc_pre_suspend(void);
136void imx_gpc_post_resume(void); 137void imx_gpc_post_resume(void);
137void imx_gpc_mask_all(void); 138void imx_gpc_mask_all(void);
138void imx_gpc_restore_all(void); 139void imx_gpc_restore_all(void);
140void imx_gpc_irq_mask(struct irq_data *d);
141void imx_gpc_irq_unmask(struct irq_data *d);
139void imx_anatop_init(void); 142void imx_anatop_init(void);
140void imx_anatop_pre_suspend(void); 143void imx_anatop_pre_suspend(void);
141void imx_anatop_post_resume(void); 144void imx_anatop_post_resume(void);
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 44a65e9ff1fc..586e0171a652 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -90,7 +90,7 @@ void imx_gpc_restore_all(void)
90 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); 90 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
91} 91}
92 92
93static void imx_gpc_irq_unmask(struct irq_data *d) 93void imx_gpc_irq_unmask(struct irq_data *d)
94{ 94{
95 void __iomem *reg; 95 void __iomem *reg;
96 u32 val; 96 u32 val;
@@ -105,7 +105,7 @@ static void imx_gpc_irq_unmask(struct irq_data *d)
105 writel_relaxed(val, reg); 105 writel_relaxed(val, reg);
106} 106}
107 107
108static void imx_gpc_irq_mask(struct irq_data *d) 108void imx_gpc_irq_mask(struct irq_data *d)
109{ 109{
110 void __iomem *reg; 110 void __iomem *reg;
111 u32 val; 111 u32 val;
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index fb7d90d36672..f303b56f087c 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -13,8 +13,12 @@
13#include <linux/delay.h> 13#include <linux/delay.h>
14#include <linux/init.h> 14#include <linux/init.h>
15#include <linux/io.h> 15#include <linux/io.h>
16#include <linux/irq.h>
17#include <linux/mfd/syscon.h>
18#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
16#include <linux/of.h> 19#include <linux/of.h>
17#include <linux/of_address.h> 20#include <linux/of_address.h>
21#include <linux/regmap.h>
18#include <linux/suspend.h> 22#include <linux/suspend.h>
19#include <asm/cacheflush.h> 23#include <asm/cacheflush.h>
20#include <asm/proc-fns.h> 24#include <asm/proc-fns.h>
@@ -116,6 +120,7 @@ static void imx6q_enable_wb(bool enable)
116 120
117int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) 121int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
118{ 122{
123 struct irq_desc *iomuxc_irq_desc;
119 u32 val = readl_relaxed(ccm_base + CLPCR); 124 u32 val = readl_relaxed(ccm_base + CLPCR);
120 125
121 val &= ~BM_CLPCR_LPM; 126 val &= ~BM_CLPCR_LPM;
@@ -144,7 +149,16 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
144 return -EINVAL; 149 return -EINVAL;
145 } 150 }
146 151
152 /*
153 * Unmask the always pending IOMUXC interrupt #32 as wakeup source to
154 * deassert dsm_request signal, so that we can ensure dsm_request
155 * is not asserted when we're going to write CLPCR register to set LPM.
156 * After setting up LPM bits, we need to mask this wakeup source.
157 */
158 iomuxc_irq_desc = irq_to_desc(32);
159 imx_gpc_irq_unmask(&iomuxc_irq_desc->irq_data);
147 writel_relaxed(val, ccm_base + CLPCR); 160 writel_relaxed(val, ccm_base + CLPCR);
161 imx_gpc_irq_mask(&iomuxc_irq_desc->irq_data);
148 162
149 return 0; 163 return 0;
150} 164}
@@ -193,8 +207,20 @@ void __init imx6q_pm_set_ccm_base(void __iomem *base)
193 207
194void __init imx6q_pm_init(void) 208void __init imx6q_pm_init(void)
195{ 209{
210 struct regmap *gpr;
211
196 WARN_ON(!ccm_base); 212 WARN_ON(!ccm_base);
197 213
214 /*
215 * Force IOMUXC irq pending, so that the interrupt to GPC can be
216 * used to deassert dsm_request signal when the signal gets
217 * asserted unexpectedly.
218 */
219 gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
220 if (!IS_ERR(gpr))
221 regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
222 IMX6Q_GPR1_GINT);
223
198 /* Set initial power mode */ 224 /* Set initial power mode */
199 imx6q_set_lpm(WAIT_CLOCKED); 225 imx6q_set_lpm(WAIT_CLOCKED);
200 226