2 * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
11 #include <linux/suspend.h>
12 #include <linux/clk.h>
14 #include <linux/err.h>
15 #include <linux/export.h>
17 #include <linux/genalloc.h>
19 #include <linux/of_address.h>
20 #include <linux/of_platform.h>
22 #include <asm/cacheflush.h>
23 #include <asm/fncpy.h>
24 #include <asm/system_misc.h>
25 #include <asm/tlbflush.h>
31 #define MXC_CCM_CLPCR 0x54
32 #define MXC_CCM_CLPCR_LPM_OFFSET 0
33 #define MXC_CCM_CLPCR_LPM_MASK 0x3
34 #define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9
35 #define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
36 #define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
38 #define MXC_CORTEXA8_PLAT_LPC 0xc
39 #define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0)
40 #define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1)
42 #define MXC_SRPG_NEON_SRPGCR 0x280
43 #define MXC_SRPG_ARM_SRPGCR 0x2a0
44 #define MXC_SRPG_EMPGC0_SRPGCR 0x2c0
45 #define MXC_SRPG_EMPGC1_SRPGCR 0x2d0
47 #define MXC_SRPGCR_PCR 1
50 * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
51 * This is also the lowest power state possible without affecting
52 * non-cpu parts of the system. For these reasons, imx5 should default
53 * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also
54 * uses this state and needs to take no action when registers remain confgiured
57 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
59 struct imx5_suspend_io_state
{
68 phys_addr_t cortex_addr
;
70 phys_addr_t m4if_addr
;
71 phys_addr_t iomuxc_addr
;
72 void (*suspend_asm
)(void __iomem
*ocram_vbase
);
73 const u32
*suspend_asm_sz
;
74 const struct imx5_suspend_io_state
*suspend_io_config
;
78 static const struct imx5_suspend_io_state imx53_suspend_io_config
[] = {
79 #define MX53_DSE_HIGHZ_MASK (0x7 << 19)
80 {.offset
= 0x584, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM0 */
81 {.offset
= 0x594, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM1 */
82 {.offset
= 0x560, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM2 */
83 {.offset
= 0x554, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM3 */
84 {.offset
= 0x574, .clear
= MX53_DSE_HIGHZ_MASK
}, /* CAS */
85 {.offset
= 0x588, .clear
= MX53_DSE_HIGHZ_MASK
}, /* RAS */
86 {.offset
= 0x578, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_0 */
87 {.offset
= 0x570, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_1 */
89 {.offset
= 0x580, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT0 */
90 {.offset
= 0x564, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT1 */
91 {.offset
= 0x57c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS0 */
92 {.offset
= 0x590, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS1 */
93 {.offset
= 0x568, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS2 */
94 {.offset
= 0x558, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDSQ3 */
95 {.offset
= 0x6f0, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_ADDS */
96 {.offset
= 0x718, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_BODS */
97 {.offset
= 0x71c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B1DS */
98 {.offset
= 0x728, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B2DS */
99 {.offset
= 0x72c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B3DS */
101 /* Controls the CKE signal which is required to leave self refresh */
102 {.offset
= 0x720, .clear
= MX53_DSE_HIGHZ_MASK
, .set
= 1 << 19}, /* CTLDS */
105 static const struct imx5_pm_data imx51_pm_data __initconst
= {
106 .ccm_addr
= 0x73fd4000,
107 .cortex_addr
= 0x83fa0000,
108 .gpc_addr
= 0x73fd8000,
111 static const struct imx5_pm_data imx53_pm_data __initconst
= {
112 .ccm_addr
= 0x53fd4000,
113 .cortex_addr
= 0x63fa0000,
114 .gpc_addr
= 0x53fd8000,
115 .m4if_addr
= 0x63fd8000,
116 .iomuxc_addr
= 0x53fa8000,
117 .suspend_asm
= &imx53_suspend
,
118 .suspend_asm_sz
= &imx53_suspend_sz
,
119 .suspend_io_config
= imx53_suspend_io_config
,
120 .suspend_io_count
= ARRAY_SIZE(imx53_suspend_io_config
),
123 #define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config)
126 * This structure is for passing necessary data for low level ocram
127 * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct
128 * definition is changed, the offset definition in that file
129 * must be also changed accordingly otherwise, the suspend to ocram
130 * function will be broken!
132 struct imx5_cpu_suspend_info
{
133 void __iomem
*m4if_base
;
134 void __iomem
*iomuxc_base
;
136 struct imx5_suspend_io_state io_state
[MX5_MAX_SUSPEND_IOSTATE
];
139 static void __iomem
*ccm_base
;
140 static void __iomem
*cortex_base
;
141 static void __iomem
*gpc_base
;
142 static void __iomem
*suspend_ocram_base
;
143 static void (*imx5_suspend_in_ocram_fn
)(void __iomem
*ocram_vbase
);
146 * set cpu low power mode before WFI instruction. This function is called
147 * mx5 because it can be used for mx51, and mx53.
149 static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode
)
151 u32 plat_lpc
, arm_srpgcr
, ccm_clpcr
;
155 /* always allow platform to issue a deep sleep mode request */
156 plat_lpc
= imx_readl(cortex_base
+ MXC_CORTEXA8_PLAT_LPC
) &
157 ~(MXC_CORTEXA8_PLAT_LPC_DSM
);
158 ccm_clpcr
= imx_readl(ccm_base
+ MXC_CCM_CLPCR
) &
159 ~(MXC_CCM_CLPCR_LPM_MASK
);
160 arm_srpgcr
= imx_readl(gpc_base
+ MXC_SRPG_ARM_SRPGCR
) &
162 empgc0
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
) &
164 empgc1
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
) &
171 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
173 case WAIT_UNCLOCKED_POWER_OFF
:
175 plat_lpc
|= MXC_CORTEXA8_PLAT_LPC_DSM
176 | MXC_CORTEXA8_PLAT_LPC_DBG_DSM
;
177 if (mode
== WAIT_UNCLOCKED_POWER_OFF
) {
178 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
179 ccm_clpcr
&= ~MXC_CCM_CLPCR_VSTBY
;
180 ccm_clpcr
&= ~MXC_CCM_CLPCR_SBYOS
;
183 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
184 ccm_clpcr
|= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET
;
185 ccm_clpcr
|= MXC_CCM_CLPCR_VSTBY
;
186 ccm_clpcr
|= MXC_CCM_CLPCR_SBYOS
;
189 arm_srpgcr
|= MXC_SRPGCR_PCR
;
192 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
195 printk(KERN_WARNING
"UNKNOWN cpu power mode: %d\n", mode
);
199 imx_writel(plat_lpc
, cortex_base
+ MXC_CORTEXA8_PLAT_LPC
);
200 imx_writel(ccm_clpcr
, ccm_base
+ MXC_CCM_CLPCR
);
201 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_ARM_SRPGCR
);
202 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_NEON_SRPGCR
);
205 empgc0
|= MXC_SRPGCR_PCR
;
206 empgc1
|= MXC_SRPGCR_PCR
;
208 imx_writel(empgc0
, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
209 imx_writel(empgc1
, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
213 static int mx5_suspend_enter(suspend_state_t state
)
217 mx5_cpu_lp_set(STOP_POWER_OFF
);
219 case PM_SUSPEND_STANDBY
:
220 /* DEFAULT_IDLE_STATE already configured */
226 if (state
== PM_SUSPEND_MEM
) {
227 local_flush_tlb_all();
230 /*clear the EMPGC0/1 bits */
231 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
232 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
234 if (imx5_suspend_in_ocram_fn
)
235 imx5_suspend_in_ocram_fn(suspend_ocram_base
);
243 /* return registers to default idle state */
244 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
248 static int mx5_pm_valid(suspend_state_t state
)
250 return (state
> PM_SUSPEND_ON
&& state
<= PM_SUSPEND_MAX
);
253 static const struct platform_suspend_ops mx5_suspend_ops
= {
254 .valid
= mx5_pm_valid
,
255 .enter
= mx5_suspend_enter
,
258 static inline int imx5_cpu_do_idle(void)
260 int ret
= tzic_enable_wake();
268 static void imx5_pm_idle(void)
273 static int __init
imx_suspend_alloc_ocram(
275 void __iomem
**virt_out
,
276 phys_addr_t
*phys_out
)
278 struct device_node
*node
;
279 struct platform_device
*pdev
;
280 struct gen_pool
*ocram_pool
;
281 unsigned long ocram_base
;
286 /* Copied from imx6: TODO factorize */
287 node
= of_find_compatible_node(NULL
, NULL
, "mmio-sram");
289 pr_warn("%s: failed to find ocram node!\n", __func__
);
293 pdev
= of_find_device_by_node(node
);
295 pr_warn("%s: failed to find ocram device!\n", __func__
);
300 ocram_pool
= gen_pool_get(&pdev
->dev
, NULL
);
302 pr_warn("%s: ocram pool unavailable!\n", __func__
);
307 ocram_base
= gen_pool_alloc(ocram_pool
, size
);
309 pr_warn("%s: unable to alloc ocram!\n", __func__
);
314 phys
= gen_pool_virt_to_phys(ocram_pool
, ocram_base
);
315 virt
= __arm_ioremap_exec(phys
, size
, false);
327 static int __init
imx5_suspend_init(const struct imx5_pm_data
*soc_data
)
329 struct imx5_cpu_suspend_info
*suspend_info
;
331 /* Need this to avoid compile error due to const typeof in fncpy.h */
332 void (*suspend_asm
)(void __iomem
*) = soc_data
->suspend_asm
;
337 if (!soc_data
->suspend_asm_sz
|| !*soc_data
->suspend_asm_sz
)
340 ret
= imx_suspend_alloc_ocram(
341 *soc_data
->suspend_asm_sz
+ sizeof(*suspend_info
),
342 &suspend_ocram_base
, NULL
);
346 suspend_info
= suspend_ocram_base
;
348 suspend_info
->io_count
= soc_data
->suspend_io_count
;
349 memcpy(suspend_info
->io_state
, soc_data
->suspend_io_config
,
350 sizeof(*suspend_info
->io_state
) * soc_data
->suspend_io_count
);
352 suspend_info
->m4if_base
= ioremap(soc_data
->m4if_addr
, SZ_16K
);
353 if (!suspend_info
->m4if_base
) {
355 goto failed_map_m4if
;
358 suspend_info
->iomuxc_base
= ioremap(soc_data
->iomuxc_addr
, SZ_16K
);
359 if (!suspend_info
->iomuxc_base
) {
361 goto failed_map_iomuxc
;
364 imx5_suspend_in_ocram_fn
= fncpy(
365 suspend_ocram_base
+ sizeof(*suspend_info
),
367 *soc_data
->suspend_asm_sz
);
372 iounmap(suspend_info
->m4if_base
);
378 static int __init
imx5_pm_common_init(const struct imx5_pm_data
*data
)
381 struct clk
*gpc_dvfs_clk
= clk_get(NULL
, "gpc_dvfs");
383 if (IS_ERR(gpc_dvfs_clk
))
384 return PTR_ERR(gpc_dvfs_clk
);
386 ret
= clk_prepare_enable(gpc_dvfs_clk
);
390 arm_pm_idle
= imx5_pm_idle
;
392 ccm_base
= ioremap(data
->ccm_addr
, SZ_16K
);
393 cortex_base
= ioremap(data
->cortex_addr
, SZ_16K
);
394 gpc_base
= ioremap(data
->gpc_addr
, SZ_16K
);
395 WARN_ON(!ccm_base
|| !cortex_base
|| !gpc_base
);
397 /* Set the registers to the default cpu idle state. */
398 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
400 ret
= imx5_cpuidle_init();
402 pr_warn("%s: cpuidle init failed %d\n", __func__
, ret
);
404 ret
= imx5_suspend_init(data
);
406 pr_warn("%s: No DDR LPM support with suspend %d!\n",
409 suspend_set_ops(&mx5_suspend_ops
);
414 void __init
imx51_pm_init(void)
416 if (IS_ENABLED(CONFIG_SOC_IMX51
))
417 imx5_pm_common_init(&imx51_pm_data
);
420 void __init
imx53_pm_init(void)
422 if (IS_ENABLED(CONFIG_SOC_IMX53
))
423 imx5_pm_common_init(&imx53_pm_data
);