* [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl
@ 2013-10-17 3:17 Shawn Guo
2013-10-17 3:17 ` [PATCH 1/4] ARM: imx6q: move low-power code out of clock driver Shawn Guo
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Shawn Guo @ 2013-10-17 3:17 UTC (permalink / raw)
To: linux-arm-kernel
The series enables suspend support for imx6dl and imx6sl with reusing
imx6q suspend code.
The patch #3 works around a defect with low power mode design which
affects all three existing imx6 SoCs, although the issue is hard to
be seen on imx6q due to its AXI fabric FIFO. FSL will publish an errata
for it soon.
Shawn Guo (4):
ARM: imx6q: move low-power code out of clock driver
ARM: imx6q: call WB and RBC configuration from imx6q_pm_enter()
ARM: imx: ensure dsm_request signal is not asserted when setting LPM
ARM: imx: enable suspend for imx6sl
arch/arm/boot/dts/imx6sl.dtsi | 3 +-
arch/arm/mach-imx/Makefile | 2 +
arch/arm/mach-imx/clk-imx6q.c | 156 +---------------------------------
arch/arm/mach-imx/clk-imx6sl.c | 3 +
arch/arm/mach-imx/common.h | 5 ++
arch/arm/mach-imx/gpc.c | 4 +-
arch/arm/mach-imx/mach-imx6sl.c | 3 +
arch/arm/mach-imx/mxc.h | 5 ++
arch/arm/mach-imx/pm-imx6q.c | 176 ++++++++++++++++++++++++++++++++++++++-
9 files changed, 199 insertions(+), 158 deletions(-)
--
1.7.9.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] ARM: imx6q: move low-power code out of clock driver
2013-10-17 3:17 [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl Shawn Guo
@ 2013-10-17 3:17 ` Shawn Guo
2013-10-17 3:17 ` [PATCH 2/4] ARM: imx6q: call WB and RBC configuration from imx6q_pm_enter() Shawn Guo
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Shawn Guo @ 2013-10-17 3:17 UTC (permalink / raw)
To: linux-arm-kernel
The LPM (Low Power Mode) code that currently sits in imx6q clock driver
will be reused by imx6sl. Let's move it into pm-imx6q.c, so that we
can keep clock driver SoC specific and reuse pm-imx6q.c on imx6sl.
In order to avoid adding another ioremap for CCM block,
imx6q_pm_set_ccm_base() is created to let clock driver set up ccm_base
for pm code.
During the move, the unused CCGR macros get removed.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
arch/arm/mach-imx/clk-imx6q.c | 156 +----------------------------------------
arch/arm/mach-imx/common.h | 2 +
arch/arm/mach-imx/pm-imx6q.c | 152 +++++++++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+), 154 deletions(-)
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 07bc0d8..d756d91 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -14,7 +14,6 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
@@ -25,155 +24,6 @@
#include "common.h"
#include "hardware.h"
-#define CCR 0x0
-#define BM_CCR_WB_COUNT (0x7 << 16)
-#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
-#define BM_CCR_RBC_EN (0x1 << 27)
-
-#define CCGR0 0x68
-#define CCGR1 0x6c
-#define CCGR2 0x70
-#define CCGR3 0x74
-#define CCGR4 0x78
-#define CCGR5 0x7c
-#define CCGR6 0x80
-#define CCGR7 0x84
-
-#define CLPCR 0x54
-#define BP_CLPCR_LPM 0
-#define BM_CLPCR_LPM (0x3 << 0)
-#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
-#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
-#define BM_CLPCR_SBYOS (0x1 << 6)
-#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
-#define BM_CLPCR_VSTBY (0x1 << 8)
-#define BP_CLPCR_STBY_COUNT 9
-#define BM_CLPCR_STBY_COUNT (0x3 << 9)
-#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
-#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
-#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
-#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
-#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
-#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
-#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
-#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
-#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
-#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
-#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
-
-#define CGPR 0x64
-#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
-
-static void __iomem *ccm_base;
-
-void imx6q_set_chicken_bit(void)
-{
- u32 val = readl_relaxed(ccm_base + CGPR);
-
- val |= BM_CGPR_CHICKEN_BIT;
- writel_relaxed(val, ccm_base + CGPR);
-}
-
-static void imx6q_enable_rbc(bool enable)
-{
- u32 val;
- static bool last_rbc_mode;
-
- if (last_rbc_mode == enable)
- return;
- /*
- * need to mask all interrupts in GPC before
- * operating RBC configurations
- */
- imx_gpc_mask_all();
-
- /* configure RBC enable bit */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_RBC_EN;
- val |= enable ? BM_CCR_RBC_EN : 0;
- writel_relaxed(val, ccm_base + CCR);
-
- /* configure RBC count */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_RBC_BYPASS_COUNT;
- val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
- writel(val, ccm_base + CCR);
-
- /*
- * need to delay@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_gpc_restore_all();
-
- last_rbc_mode = enable;
-}
-
-static void imx6q_enable_wb(bool enable)
-{
- u32 val;
- static bool last_wb_mode;
-
- if (last_wb_mode == enable)
- return;
-
- /* configure well bias enable bit */
- val = readl_relaxed(ccm_base + CLPCR);
- val &= ~BM_CLPCR_WB_PER_AT_LPM;
- val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
- writel_relaxed(val, ccm_base + CLPCR);
-
- /* configure well bias count */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_WB_COUNT;
- val |= enable ? BM_CCR_WB_COUNT : 0;
- writel_relaxed(val, ccm_base + CCR);
-
- last_wb_mode = enable;
-}
-
-int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
-{
- u32 val = readl_relaxed(ccm_base + CLPCR);
-
- val &= ~BM_CLPCR_LPM;
- switch (mode) {
- case WAIT_CLOCKED:
- imx6q_enable_wb(false);
- imx6q_enable_rbc(false);
- break;
- case WAIT_UNCLOCKED:
- val |= 0x1 << BP_CLPCR_LPM;
- val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
- break;
- case STOP_POWER_ON:
- val |= 0x2 << BP_CLPCR_LPM;
- break;
- case WAIT_UNCLOCKED_POWER_OFF:
- val |= 0x1 << BP_CLPCR_LPM;
- val &= ~BM_CLPCR_VSTBY;
- val &= ~BM_CLPCR_SBYOS;
- break;
- case STOP_POWER_OFF:
- val |= 0x2 << BP_CLPCR_LPM;
- val |= 0x3 << BP_CLPCR_STBY_COUNT;
- val |= BM_CLPCR_VSTBY;
- val |= BM_CLPCR_SBYOS;
- imx6q_enable_wb(true);
- imx6q_enable_rbc(true);
- break;
- default:
- return -EINVAL;
- }
-
- writel_relaxed(val, ccm_base + CLPCR);
-
- return 0;
-}
-
static const char *step_sels[] = { "osc", "pll2_pfd2_396m", };
static const char *pll1_sw_sels[] = { "pll1_sys", "step", };
static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", };
@@ -384,7 +234,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
np = ccm_node;
base = of_iomap(np, 0);
WARN_ON(!base);
- ccm_base = base;
+
+ imx6q_pm_set_ccm_base(base);
/* name reg shift width parent_names num_parents */
clk[step] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels));
@@ -627,9 +478,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
if (IS_ENABLED(CONFIG_PCI_IMX6))
clk_set_parent(clk[lvds1_sel], clk[sata_ref]);
- /* Set initial power mode */
- imx6q_set_lpm(WAIT_CLOCKED);
-
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpt");
base = of_iomap(np, 0);
WARN_ON(!base);
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index c1fe6d5..2b4ce09 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -151,9 +151,11 @@ int imx_cpu_kill(unsigned int cpu);
#ifdef CONFIG_PM
void imx6q_pm_init(void);
+void imx6q_pm_set_ccm_base(void __iomem *base);
void imx5_pm_init(void);
#else
static inline void imx6q_pm_init(void) {}
+static inline void imx6q_pm_set_ccm_base(void __iomem *base) {}
static inline void imx5_pm_init(void) {}
#endif
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index 2049427..461a895 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -10,9 +10,11 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/proc-fns.h>
@@ -22,6 +24,146 @@
#include "common.h"
#include "hardware.h"
+#define CCR 0x0
+#define BM_CCR_WB_COUNT (0x7 << 16)
+#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
+#define BM_CCR_RBC_EN (0x1 << 27)
+
+#define CLPCR 0x54
+#define BP_CLPCR_LPM 0
+#define BM_CLPCR_LPM (0x3 << 0)
+#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
+#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
+#define BM_CLPCR_SBYOS (0x1 << 6)
+#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
+#define BM_CLPCR_VSTBY (0x1 << 8)
+#define BP_CLPCR_STBY_COUNT 9
+#define BM_CLPCR_STBY_COUNT (0x3 << 9)
+#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
+#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
+#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
+#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
+#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
+#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
+#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
+#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
+#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
+#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
+#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
+
+#define CGPR 0x64
+#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
+
+static void __iomem *ccm_base;
+
+void imx6q_set_chicken_bit(void)
+{
+ u32 val = readl_relaxed(ccm_base + CGPR);
+
+ val |= BM_CGPR_CHICKEN_BIT;
+ writel_relaxed(val, ccm_base + CGPR);
+}
+
+static void imx6q_enable_rbc(bool enable)
+{
+ u32 val;
+ static bool last_rbc_mode;
+
+ if (last_rbc_mode == enable)
+ return;
+ /*
+ * need to mask all interrupts in GPC before
+ * operating RBC configurations
+ */
+ imx_gpc_mask_all();
+
+ /* configure RBC enable bit */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_EN;
+ val |= enable ? BM_CCR_RBC_EN : 0;
+ writel_relaxed(val, ccm_base + CCR);
+
+ /* configure RBC count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_BYPASS_COUNT;
+ val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
+ writel(val, ccm_base + CCR);
+
+ /*
+ * need to delay@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_gpc_restore_all();
+
+ last_rbc_mode = enable;
+}
+
+static void imx6q_enable_wb(bool enable)
+{
+ u32 val;
+ static bool last_wb_mode;
+
+ if (last_wb_mode == enable)
+ return;
+
+ /* configure well bias enable bit */
+ val = readl_relaxed(ccm_base + CLPCR);
+ val &= ~BM_CLPCR_WB_PER_AT_LPM;
+ val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
+ writel_relaxed(val, ccm_base + CLPCR);
+
+ /* configure well bias count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_WB_COUNT;
+ val |= enable ? BM_CCR_WB_COUNT : 0;
+ writel_relaxed(val, ccm_base + CCR);
+
+ last_wb_mode = enable;
+}
+
+int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
+{
+ u32 val = readl_relaxed(ccm_base + CLPCR);
+
+ val &= ~BM_CLPCR_LPM;
+ switch (mode) {
+ case WAIT_CLOCKED:
+ imx6q_enable_wb(false);
+ imx6q_enable_rbc(false);
+ break;
+ case WAIT_UNCLOCKED:
+ val |= 0x1 << BP_CLPCR_LPM;
+ val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
+ break;
+ case STOP_POWER_ON:
+ val |= 0x2 << BP_CLPCR_LPM;
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ val |= 0x1 << BP_CLPCR_LPM;
+ val &= ~BM_CLPCR_VSTBY;
+ val &= ~BM_CLPCR_SBYOS;
+ break;
+ case STOP_POWER_OFF:
+ val |= 0x2 << BP_CLPCR_LPM;
+ val |= 0x3 << BP_CLPCR_STBY_COUNT;
+ val |= BM_CLPCR_VSTBY;
+ val |= BM_CLPCR_SBYOS;
+ imx6q_enable_wb(true);
+ imx6q_enable_rbc(true);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(val, ccm_base + CLPCR);
+
+ return 0;
+}
+
static int imx6q_suspend_finish(unsigned long val)
{
cpu_do_idle();
@@ -55,7 +197,17 @@ static const struct platform_suspend_ops imx6q_pm_ops = {
.valid = suspend_valid_only_mem,
};
+void __init imx6q_pm_set_ccm_base(void __iomem *base)
+{
+ ccm_base = base;
+}
+
void __init imx6q_pm_init(void)
{
+ WARN_ON(!ccm_base);
+
+ /* Set initial power mode */
+ imx6q_set_lpm(WAIT_CLOCKED);
+
suspend_set_ops(&imx6q_pm_ops);
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] ARM: imx6q: call WB and RBC configuration from imx6q_pm_enter()
2013-10-17 3:17 [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl Shawn Guo
2013-10-17 3:17 ` [PATCH 1/4] ARM: imx6q: move low-power code out of clock driver Shawn Guo
@ 2013-10-17 3:17 ` Shawn Guo
2013-10-17 3:17 ` [PATCH 3/4] ARM: imx: ensure dsm_request signal is not asserted when setting LPM Shawn Guo
2013-10-17 3:17 ` [PATCH 4/4] ARM: imx: enable suspend for imx6sl Shawn Guo
3 siblings, 0 replies; 5+ messages in thread
From: Shawn Guo @ 2013-10-17 3:17 UTC (permalink / raw)
To: linux-arm-kernel
The WB and RBC configuration calls are currently made from
imx6q_set_lpm() for WAIT_CLOCKED and WAIT_UNCLOCKED mode with a simple
state tracking. This becomes unnecessary since we can make the calls
from imx6q_pm_enter() directly now for suspend.
More importantly, the current call of imx6q_enable_wb() from
imx6q_set_lpm() is buggy. The CLPCR register bits configured by
imx6q_enable_wb() will get lost, because imx6q_set_lpm() caches the same
register and write it back at the end of the function. That's why the
imx6dl suspend/resume does not work currently - the wakeup from suspend
triggers a reset on imx6dl.
Moves the WB and RBC calls into imx6q_pm_enter() to save the state
tracking and fixes above bug, so that suspend/resume can start working
on imx6dl.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
arch/arm/mach-imx/pm-imx6q.c | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index 461a895..fb7d90d 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -67,10 +67,7 @@ void imx6q_set_chicken_bit(void)
static void imx6q_enable_rbc(bool enable)
{
u32 val;
- static bool last_rbc_mode;
- if (last_rbc_mode == enable)
- return;
/*
* need to mask all interrupts in GPC before
* operating RBC configurations
@@ -98,17 +95,11 @@ static void imx6q_enable_rbc(bool enable)
/* restore GPC interrupt mask settings */
imx_gpc_restore_all();
-
- last_rbc_mode = enable;
}
static void imx6q_enable_wb(bool enable)
{
u32 val;
- static bool last_wb_mode;
-
- if (last_wb_mode == enable)
- return;
/* configure well bias enable bit */
val = readl_relaxed(ccm_base + CLPCR);
@@ -121,8 +112,6 @@ static void imx6q_enable_wb(bool enable)
val &= ~BM_CCR_WB_COUNT;
val |= enable ? BM_CCR_WB_COUNT : 0;
writel_relaxed(val, ccm_base + CCR);
-
- last_wb_mode = enable;
}
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
@@ -132,8 +121,6 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
val &= ~BM_CLPCR_LPM;
switch (mode) {
case WAIT_CLOCKED:
- imx6q_enable_wb(false);
- imx6q_enable_rbc(false);
break;
case WAIT_UNCLOCKED:
val |= 0x1 << BP_CLPCR_LPM;
@@ -152,8 +139,6 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
- imx6q_enable_wb(true);
- imx6q_enable_rbc(true);
break;
default:
return -EINVAL;
@@ -175,6 +160,8 @@ static int imx6q_pm_enter(suspend_state_t state)
switch (state) {
case PM_SUSPEND_MEM:
imx6q_set_lpm(STOP_POWER_OFF);
+ imx6q_enable_wb(true);
+ imx6q_enable_rbc(true);
imx_gpc_pre_suspend();
imx_anatop_pre_suspend();
imx_set_cpu_jump(0, v7_cpu_resume);
@@ -183,6 +170,8 @@ static int imx6q_pm_enter(suspend_state_t state)
imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
+ imx6q_enable_rbc(false);
+ imx6q_enable_wb(false);
imx6q_set_lpm(WAIT_CLOCKED);
break;
default:
--
1.7.9.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] ARM: imx: ensure dsm_request signal is not asserted when setting LPM
2013-10-17 3:17 [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl Shawn Guo
2013-10-17 3:17 ` [PATCH 1/4] ARM: imx6q: move low-power code out of clock driver Shawn Guo
2013-10-17 3:17 ` [PATCH 2/4] ARM: imx6q: call WB and RBC configuration from imx6q_pm_enter() Shawn Guo
@ 2013-10-17 3:17 ` Shawn Guo
2013-10-17 3:17 ` [PATCH 4/4] ARM: imx: enable suspend for imx6sl Shawn Guo
3 siblings, 0 replies; 5+ messages in thread
From: Shawn Guo @ 2013-10-17 3:17 UTC (permalink / raw)
To: linux-arm-kernel
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>
---
arch/arm/mach-imx/common.h | 3 +++
arch/arm/mach-imx/gpc.c | 4 ++--
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 2b4ce09..ce81962 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -13,6 +13,7 @@
#include <linux/reboot.h>
+struct irq_data;
struct platform_device;
struct pt_regs;
struct clk;
@@ -140,6 +141,8 @@ void imx_gpc_pre_suspend(void);
void imx_gpc_post_resume(void);
void imx_gpc_mask_all(void);
void imx_gpc_restore_all(void);
+void imx_gpc_irq_mask(struct irq_data *d);
+void imx_gpc_irq_unmask(struct irq_data *d);
void imx_anatop_init(void);
void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void);
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 44a65e9..586e017 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)
writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
}
-static void imx_gpc_irq_unmask(struct irq_data *d)
+void imx_gpc_irq_unmask(struct irq_data *d)
{
void __iomem *reg;
u32 val;
@@ -105,7 +105,7 @@ static void imx_gpc_irq_unmask(struct irq_data *d)
writel_relaxed(val, reg);
}
-static void imx_gpc_irq_mask(struct irq_data *d)
+void imx_gpc_irq_mask(struct irq_data *d)
{
void __iomem *reg;
u32 val;
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index fb7d90d..f303b56 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -13,8 +13,12 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/regmap.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/proc-fns.h>
@@ -116,6 +120,7 @@ static void imx6q_enable_wb(bool enable)
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
{
+ struct irq_desc *iomuxc_irq_desc;
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
@@ -144,7 +149,16 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
return -EINVAL;
}
+ /*
+ * Unmask the always pending IOMUXC interrupt #32 as wakeup source to
+ * deassert dsm_request signal, so that we can ensure dsm_request
+ * is not asserted when we're going to write CLPCR register to set LPM.
+ * After setting up LPM bits, we need to mask this wakeup source.
+ */
+ iomuxc_irq_desc = irq_to_desc(32);
+ imx_gpc_irq_unmask(&iomuxc_irq_desc->irq_data);
writel_relaxed(val, ccm_base + CLPCR);
+ imx_gpc_irq_mask(&iomuxc_irq_desc->irq_data);
return 0;
}
@@ -193,8 +207,20 @@ void __init imx6q_pm_set_ccm_base(void __iomem *base)
void __init imx6q_pm_init(void)
{
+ struct regmap *gpr;
+
WARN_ON(!ccm_base);
+ /*
+ * Force IOMUXC irq pending, so that the interrupt to GPC can be
+ * used to deassert dsm_request signal when the signal gets
+ * asserted unexpectedly.
+ */
+ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (!IS_ERR(gpr))
+ regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
+ IMX6Q_GPR1_GINT);
+
/* Set initial power mode */
imx6q_set_lpm(WAIT_CLOCKED);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] ARM: imx: enable suspend for imx6sl
2013-10-17 3:17 [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl Shawn Guo
` (2 preceding siblings ...)
2013-10-17 3:17 ` [PATCH 3/4] ARM: imx: ensure dsm_request signal is not asserted when setting LPM Shawn Guo
@ 2013-10-17 3:17 ` Shawn Guo
3 siblings, 0 replies; 5+ messages in thread
From: Shawn Guo @ 2013-10-17 3:17 UTC (permalink / raw)
To: linux-arm-kernel
The imx6sl low power mode implementation inherits imx6q/dl one,
and pm-imx6q.c can just work for imx6sl with some minor updates.
Let's enable imx6sl suspend support by reusing pm-imx6q.c and use
cpu_is_imxXX() to handle the those minor differences between imx6sl
and imx6q/dl.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
arch/arm/boot/dts/imx6sl.dtsi | 3 ++-
arch/arm/mach-imx/Makefile | 2 ++
arch/arm/mach-imx/clk-imx6sl.c | 3 +++
arch/arm/mach-imx/mach-imx6sl.c | 3 +++
arch/arm/mach-imx/mxc.h | 5 +++++
arch/arm/mach-imx/pm-imx6q.c | 9 ++++++++-
6 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index 17607b3..f2f6df6 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -535,7 +535,8 @@
};
gpr: iomuxc-gpr at 020e0000 {
- compatible = "fsl,imx6sl-iomuxc-gpr", "syscon";
+ compatible = "fsl,imx6sl-iomuxc-gpr",
+ "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e0000 0x38>;
};
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 5383c58..bbe1f5b 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -102,6 +102,8 @@ obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
+# i.MX6SL reuses pm-imx6q.c
+obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o
endif
# i.MX5 based machines
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c
index a5c3c5d..c0c4ef5 100644
--- a/arch/arm/mach-imx/clk-imx6sl.c
+++ b/arch/arm/mach-imx/clk-imx6sl.c
@@ -127,6 +127,9 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
base = of_iomap(np, 0);
WARN_ON(!base);
+ /* Reuse imx6q pm code */
+ imx6q_pm_set_ccm_base(base);
+
/* name reg shift width parent_names num_parents */
clks[IMX6SL_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels));
clks[IMX6SL_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels));
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c
index 5c767f0..c7502f2 100644
--- a/arch/arm/mach-imx/mach-imx6sl.c
+++ b/arch/arm/mach-imx/mach-imx6sl.c
@@ -48,6 +48,9 @@ static void __init imx6sl_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, parent);
imx6sl_fec_init();
+ imx_anatop_init();
+ /* Reuse imx6q pm code */
+ imx6q_pm_init();
}
static void __init imx6sl_init_irq(void)
diff --git a/arch/arm/mach-imx/mxc.h b/arch/arm/mach-imx/mxc.h
index 99e03ea..b08ab3a 100644
--- a/arch/arm/mach-imx/mxc.h
+++ b/arch/arm/mach-imx/mxc.h
@@ -153,6 +153,11 @@ extern unsigned int __mxc_cpu_type;
#endif
#ifndef __ASSEMBLY__
+static inline bool cpu_is_imx6sl(void)
+{
+ return __mxc_cpu_type == MXC_CPU_IMX6SL;
+}
+
static inline bool cpu_is_imx6dl(void)
{
return __mxc_cpu_type == MXC_CPU_IMX6DL;
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index f303b56..aecd9f8 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -144,6 +144,12 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
+ if (cpu_is_imx6sl()) {
+ val |= BM_CLPCR_BYPASS_PMIC_READY;
+ val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ } else {
+ val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ }
break;
default:
return -EINVAL;
@@ -181,7 +187,8 @@ static int imx6q_pm_enter(suspend_state_t state)
imx_set_cpu_jump(0, v7_cpu_resume);
/* Zzz ... */
cpu_suspend(0, imx6q_suspend_finish);
- imx_smp_prepare();
+ if (cpu_is_imx6q() || cpu_is_imx6dl())
+ imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
imx6q_enable_rbc(false);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-10-17 3:17 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-17 3:17 [PATCH 0/4] ARM: imx: suspend support for imx6dl and imx6sl Shawn Guo
2013-10-17 3:17 ` [PATCH 1/4] ARM: imx6q: move low-power code out of clock driver Shawn Guo
2013-10-17 3:17 ` [PATCH 2/4] ARM: imx6q: call WB and RBC configuration from imx6q_pm_enter() Shawn Guo
2013-10-17 3:17 ` [PATCH 3/4] ARM: imx: ensure dsm_request signal is not asserted when setting LPM Shawn Guo
2013-10-17 3:17 ` [PATCH 4/4] ARM: imx: enable suspend for imx6sl Shawn Guo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).