diff options
author | Shawn Guo <shawn.guo@linaro.org> | 2013-10-16 07:52:00 -0400 |
---|---|---|
committer | Shawn Guo <shawn.guo@linaro.org> | 2013-10-20 21:39:26 -0400 |
commit | d48866fefdac239a4e02777e712aad60db9ee8a8 (patch) | |
tree | be0bb666ba9a8d0c29a262407e885ee91abb85e7 | |
parent | 1d674a73c59211cc33cb9c2954659033d8458fa9 (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.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/pm-imx6q.c | 26 |
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 | ||
16 | struct irq_data; | ||
16 | struct platform_device; | 17 | struct platform_device; |
17 | struct pt_regs; | 18 | struct pt_regs; |
18 | struct clk; | 19 | struct clk; |
@@ -136,6 +137,8 @@ void imx_gpc_pre_suspend(void); | |||
136 | void imx_gpc_post_resume(void); | 137 | void imx_gpc_post_resume(void); |
137 | void imx_gpc_mask_all(void); | 138 | void imx_gpc_mask_all(void); |
138 | void imx_gpc_restore_all(void); | 139 | void imx_gpc_restore_all(void); |
140 | void imx_gpc_irq_mask(struct irq_data *d); | ||
141 | void imx_gpc_irq_unmask(struct irq_data *d); | ||
139 | void imx_anatop_init(void); | 142 | void imx_anatop_init(void); |
140 | void imx_anatop_pre_suspend(void); | 143 | void imx_anatop_pre_suspend(void); |
141 | void imx_anatop_post_resume(void); | 144 | void 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 | ||
93 | static void imx_gpc_irq_unmask(struct irq_data *d) | 93 | void 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 | ||
108 | static void imx_gpc_irq_mask(struct irq_data *d) | 108 | void 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 | ||
117 | int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) | 121 | int 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 | ||
194 | void __init imx6q_pm_init(void) | 208 | void __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 | ||