diff options
Diffstat (limited to 'arch/arm/mach-imx/pm-imx6q.c')
-rw-r--r-- | arch/arm/mach-imx/pm-imx6q.c | 176 |
1 files changed, 175 insertions, 1 deletions
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c index 204942749e21..aecd9f8037e0 100644 --- a/arch/arm/mach-imx/pm-imx6q.c +++ b/arch/arm/mach-imx/pm-imx6q.c | |||
@@ -10,9 +10,15 @@ | |||
10 | * http://www.gnu.org/copyleft/gpl.html | 10 | * http://www.gnu.org/copyleft/gpl.html |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/delay.h> | ||
13 | #include <linux/init.h> | 14 | #include <linux/init.h> |
14 | #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> | ||
15 | #include <linux/of.h> | 19 | #include <linux/of.h> |
20 | #include <linux/of_address.h> | ||
21 | #include <linux/regmap.h> | ||
16 | #include <linux/suspend.h> | 22 | #include <linux/suspend.h> |
17 | #include <asm/cacheflush.h> | 23 | #include <asm/cacheflush.h> |
18 | #include <asm/proc-fns.h> | 24 | #include <asm/proc-fns.h> |
@@ -22,6 +28,147 @@ | |||
22 | #include "common.h" | 28 | #include "common.h" |
23 | #include "hardware.h" | 29 | #include "hardware.h" |
24 | 30 | ||
31 | #define CCR 0x0 | ||
32 | #define BM_CCR_WB_COUNT (0x7 << 16) | ||
33 | #define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21) | ||
34 | #define BM_CCR_RBC_EN (0x1 << 27) | ||
35 | |||
36 | #define CLPCR 0x54 | ||
37 | #define BP_CLPCR_LPM 0 | ||
38 | #define BM_CLPCR_LPM (0x3 << 0) | ||
39 | #define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2) | ||
40 | #define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) | ||
41 | #define BM_CLPCR_SBYOS (0x1 << 6) | ||
42 | #define BM_CLPCR_DIS_REF_OSC (0x1 << 7) | ||
43 | #define BM_CLPCR_VSTBY (0x1 << 8) | ||
44 | #define BP_CLPCR_STBY_COUNT 9 | ||
45 | #define BM_CLPCR_STBY_COUNT (0x3 << 9) | ||
46 | #define BM_CLPCR_COSC_PWRDOWN (0x1 << 11) | ||
47 | #define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16) | ||
48 | #define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17) | ||
49 | #define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19) | ||
50 | #define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21) | ||
51 | #define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) | ||
52 | #define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) | ||
53 | #define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24) | ||
54 | #define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25) | ||
55 | #define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) | ||
56 | #define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) | ||
57 | |||
58 | #define CGPR 0x64 | ||
59 | #define BM_CGPR_CHICKEN_BIT (0x1 << 17) | ||
60 | |||
61 | static void __iomem *ccm_base; | ||
62 | |||
63 | void imx6q_set_chicken_bit(void) | ||
64 | { | ||
65 | u32 val = readl_relaxed(ccm_base + CGPR); | ||
66 | |||
67 | val |= BM_CGPR_CHICKEN_BIT; | ||
68 | writel_relaxed(val, ccm_base + CGPR); | ||
69 | } | ||
70 | |||
71 | static void imx6q_enable_rbc(bool enable) | ||
72 | { | ||
73 | u32 val; | ||
74 | |||
75 | /* | ||
76 | * need to mask all interrupts in GPC before | ||
77 | * operating RBC configurations | ||
78 | */ | ||
79 | imx_gpc_mask_all(); | ||
80 | |||
81 | /* configure RBC enable bit */ | ||
82 | val = readl_relaxed(ccm_base + CCR); | ||
83 | val &= ~BM_CCR_RBC_EN; | ||
84 | val |= enable ? BM_CCR_RBC_EN : 0; | ||
85 | writel_relaxed(val, ccm_base + CCR); | ||
86 | |||
87 | /* configure RBC count */ | ||
88 | val = readl_relaxed(ccm_base + CCR); | ||
89 | val &= ~BM_CCR_RBC_BYPASS_COUNT; | ||
90 | val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0; | ||
91 | writel(val, ccm_base + CCR); | ||
92 | |||
93 | /* | ||
94 | * need to delay at least 2 cycles of CKIL(32K) | ||
95 | * due to hardware design requirement, which is | ||
96 | * ~61us, here we use 65us for safe | ||
97 | */ | ||
98 | udelay(65); | ||
99 | |||
100 | /* restore GPC interrupt mask settings */ | ||
101 | imx_gpc_restore_all(); | ||
102 | } | ||
103 | |||
104 | static void imx6q_enable_wb(bool enable) | ||
105 | { | ||
106 | u32 val; | ||
107 | |||
108 | /* configure well bias enable bit */ | ||
109 | val = readl_relaxed(ccm_base + CLPCR); | ||
110 | val &= ~BM_CLPCR_WB_PER_AT_LPM; | ||
111 | val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0; | ||
112 | writel_relaxed(val, ccm_base + CLPCR); | ||
113 | |||
114 | /* configure well bias count */ | ||
115 | val = readl_relaxed(ccm_base + CCR); | ||
116 | val &= ~BM_CCR_WB_COUNT; | ||
117 | val |= enable ? BM_CCR_WB_COUNT : 0; | ||
118 | writel_relaxed(val, ccm_base + CCR); | ||
119 | } | ||
120 | |||
121 | int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) | ||
122 | { | ||
123 | struct irq_desc *iomuxc_irq_desc; | ||
124 | u32 val = readl_relaxed(ccm_base + CLPCR); | ||
125 | |||
126 | val &= ~BM_CLPCR_LPM; | ||
127 | switch (mode) { | ||
128 | case WAIT_CLOCKED: | ||
129 | break; | ||
130 | case WAIT_UNCLOCKED: | ||
131 | val |= 0x1 << BP_CLPCR_LPM; | ||
132 | val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; | ||
133 | break; | ||
134 | case STOP_POWER_ON: | ||
135 | val |= 0x2 << BP_CLPCR_LPM; | ||
136 | break; | ||
137 | case WAIT_UNCLOCKED_POWER_OFF: | ||
138 | val |= 0x1 << BP_CLPCR_LPM; | ||
139 | val &= ~BM_CLPCR_VSTBY; | ||
140 | val &= ~BM_CLPCR_SBYOS; | ||
141 | break; | ||
142 | case STOP_POWER_OFF: | ||
143 | val |= 0x2 << BP_CLPCR_LPM; | ||
144 | val |= 0x3 << BP_CLPCR_STBY_COUNT; | ||
145 | val |= BM_CLPCR_VSTBY; | ||
146 | val |= BM_CLPCR_SBYOS; | ||
147 | if (cpu_is_imx6sl()) { | ||
148 | val |= BM_CLPCR_BYPASS_PMIC_READY; | ||
149 | val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; | ||
150 | } else { | ||
151 | val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; | ||
152 | } | ||
153 | break; | ||
154 | default: | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Unmask the always pending IOMUXC interrupt #32 as wakeup source to | ||
160 | * deassert dsm_request signal, so that we can ensure dsm_request | ||
161 | * is not asserted when we're going to write CLPCR register to set LPM. | ||
162 | * After setting up LPM bits, we need to mask this wakeup source. | ||
163 | */ | ||
164 | iomuxc_irq_desc = irq_to_desc(32); | ||
165 | imx_gpc_irq_unmask(&iomuxc_irq_desc->irq_data); | ||
166 | writel_relaxed(val, ccm_base + CLPCR); | ||
167 | imx_gpc_irq_mask(&iomuxc_irq_desc->irq_data); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
25 | static int imx6q_suspend_finish(unsigned long val) | 172 | static int imx6q_suspend_finish(unsigned long val) |
26 | { | 173 | { |
27 | cpu_do_idle(); | 174 | cpu_do_idle(); |
@@ -33,14 +180,19 @@ static int imx6q_pm_enter(suspend_state_t state) | |||
33 | switch (state) { | 180 | switch (state) { |
34 | case PM_SUSPEND_MEM: | 181 | case PM_SUSPEND_MEM: |
35 | imx6q_set_lpm(STOP_POWER_OFF); | 182 | imx6q_set_lpm(STOP_POWER_OFF); |
183 | imx6q_enable_wb(true); | ||
184 | imx6q_enable_rbc(true); | ||
36 | imx_gpc_pre_suspend(); | 185 | imx_gpc_pre_suspend(); |
37 | imx_anatop_pre_suspend(); | 186 | imx_anatop_pre_suspend(); |
38 | imx_set_cpu_jump(0, v7_cpu_resume); | 187 | imx_set_cpu_jump(0, v7_cpu_resume); |
39 | /* Zzz ... */ | 188 | /* Zzz ... */ |
40 | cpu_suspend(0, imx6q_suspend_finish); | 189 | cpu_suspend(0, imx6q_suspend_finish); |
41 | imx_smp_prepare(); | 190 | if (cpu_is_imx6q() || cpu_is_imx6dl()) |
191 | imx_smp_prepare(); | ||
42 | imx_anatop_post_resume(); | 192 | imx_anatop_post_resume(); |
43 | imx_gpc_post_resume(); | 193 | imx_gpc_post_resume(); |
194 | imx6q_enable_rbc(false); | ||
195 | imx6q_enable_wb(false); | ||
44 | imx6q_set_lpm(WAIT_CLOCKED); | 196 | imx6q_set_lpm(WAIT_CLOCKED); |
45 | break; | 197 | break; |
46 | default: | 198 | default: |
@@ -55,7 +207,29 @@ static const struct platform_suspend_ops imx6q_pm_ops = { | |||
55 | .valid = suspend_valid_only_mem, | 207 | .valid = suspend_valid_only_mem, |
56 | }; | 208 | }; |
57 | 209 | ||
210 | void __init imx6q_pm_set_ccm_base(void __iomem *base) | ||
211 | { | ||
212 | ccm_base = base; | ||
213 | } | ||
214 | |||
58 | void __init imx6q_pm_init(void) | 215 | void __init imx6q_pm_init(void) |
59 | { | 216 | { |
217 | struct regmap *gpr; | ||
218 | |||
219 | WARN_ON(!ccm_base); | ||
220 | |||
221 | /* | ||
222 | * Force IOMUXC irq pending, so that the interrupt to GPC can be | ||
223 | * used to deassert dsm_request signal when the signal gets | ||
224 | * asserted unexpectedly. | ||
225 | */ | ||
226 | gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); | ||
227 | if (!IS_ERR(gpr)) | ||
228 | regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT, | ||
229 | IMX6Q_GPR1_GINT); | ||
230 | |||
231 | /* Set initial power mode */ | ||
232 | imx6q_set_lpm(WAIT_CLOCKED); | ||
233 | |||
60 | suspend_set_ops(&imx6q_pm_ops); | 234 | suspend_set_ops(&imx6q_pm_ops); |
61 | } | 235 | } |