1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
5 #include <linux/suspend.h>
9 #include <linux/export.h>
11 #include <linux/genalloc.h>
13 #include <linux/of_address.h>
14 #include <linux/of_platform.h>
16 #include <asm/cacheflush.h>
17 #include <asm/fncpy.h>
18 #include <asm/system_misc.h>
19 #include <asm/tlbflush.h>
25 #define MXC_CCM_CLPCR 0x54
26 #define MXC_CCM_CLPCR_LPM_OFFSET 0
27 #define MXC_CCM_CLPCR_LPM_MASK 0x3
28 #define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9
29 #define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
30 #define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
32 #define MXC_CORTEXA8_PLAT_LPC 0xc
33 #define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0)
34 #define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1)
36 #define MXC_SRPG_NEON_SRPGCR 0x280
37 #define MXC_SRPG_ARM_SRPGCR 0x2a0
38 #define MXC_SRPG_EMPGC0_SRPGCR 0x2c0
39 #define MXC_SRPG_EMPGC1_SRPGCR 0x2d0
41 #define MXC_SRPGCR_PCR 1
44 * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
45 * This is also the lowest power state possible without affecting
46 * non-cpu parts of the system. For these reasons, imx5 should default
47 * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also
48 * uses this state and needs to take no action when registers remain confgiured
51 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
53 struct imx5_suspend_io_state
{
62 phys_addr_t cortex_addr
;
64 phys_addr_t m4if_addr
;
65 phys_addr_t iomuxc_addr
;
66 void (*suspend_asm
)(void __iomem
*ocram_vbase
);
67 const u32
*suspend_asm_sz
;
68 const struct imx5_suspend_io_state
*suspend_io_config
;
72 static const struct imx5_suspend_io_state imx53_suspend_io_config
[] = {
73 #define MX53_DSE_HIGHZ_MASK (0x7 << 19)
74 {.offset
= 0x584, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM0 */
75 {.offset
= 0x594, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM1 */
76 {.offset
= 0x560, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM2 */
77 {.offset
= 0x554, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM3 */
78 {.offset
= 0x574, .clear
= MX53_DSE_HIGHZ_MASK
}, /* CAS */
79 {.offset
= 0x588, .clear
= MX53_DSE_HIGHZ_MASK
}, /* RAS */
80 {.offset
= 0x578, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_0 */
81 {.offset
= 0x570, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_1 */
83 {.offset
= 0x580, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT0 */
84 {.offset
= 0x564, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT1 */
85 {.offset
= 0x57c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS0 */
86 {.offset
= 0x590, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS1 */
87 {.offset
= 0x568, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS2 */
88 {.offset
= 0x558, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDSQ3 */
89 {.offset
= 0x6f0, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_ADDS */
90 {.offset
= 0x718, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_BODS */
91 {.offset
= 0x71c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B1DS */
92 {.offset
= 0x728, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B2DS */
93 {.offset
= 0x72c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B3DS */
95 /* Controls the CKE signal which is required to leave self refresh */
96 {.offset
= 0x720, .clear
= MX53_DSE_HIGHZ_MASK
, .set
= 1 << 19}, /* CTLDS */
99 static const struct imx5_pm_data imx51_pm_data __initconst
= {
100 .ccm_addr
= 0x73fd4000,
101 .cortex_addr
= 0x83fa0000,
102 .gpc_addr
= 0x73fd8000,
105 static const struct imx5_pm_data imx53_pm_data __initconst
= {
106 .ccm_addr
= 0x53fd4000,
107 .cortex_addr
= 0x63fa0000,
108 .gpc_addr
= 0x53fd8000,
109 .m4if_addr
= 0x63fd8000,
110 .iomuxc_addr
= 0x53fa8000,
111 .suspend_asm
= &imx53_suspend
,
112 .suspend_asm_sz
= &imx53_suspend_sz
,
113 .suspend_io_config
= imx53_suspend_io_config
,
114 .suspend_io_count
= ARRAY_SIZE(imx53_suspend_io_config
),
117 #define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config)
120 * This structure is for passing necessary data for low level ocram
121 * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct
122 * definition is changed, the offset definition in that file
123 * must be also changed accordingly otherwise, the suspend to ocram
124 * function will be broken!
126 struct imx5_cpu_suspend_info
{
127 void __iomem
*m4if_base
;
128 void __iomem
*iomuxc_base
;
130 struct imx5_suspend_io_state io_state
[MX5_MAX_SUSPEND_IOSTATE
];
133 static void __iomem
*ccm_base
;
134 static void __iomem
*cortex_base
;
135 static void __iomem
*gpc_base
;
136 static void __iomem
*suspend_ocram_base
;
137 static void (*imx5_suspend_in_ocram_fn
)(void __iomem
*ocram_vbase
);
140 * set cpu low power mode before WFI instruction. This function is called
141 * mx5 because it can be used for mx51, and mx53.
143 static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode
)
145 u32 plat_lpc
, arm_srpgcr
, ccm_clpcr
;
149 /* always allow platform to issue a deep sleep mode request */
150 plat_lpc
= imx_readl(cortex_base
+ MXC_CORTEXA8_PLAT_LPC
) &
151 ~(MXC_CORTEXA8_PLAT_LPC_DSM
);
152 ccm_clpcr
= imx_readl(ccm_base
+ MXC_CCM_CLPCR
) &
153 ~(MXC_CCM_CLPCR_LPM_MASK
);
154 arm_srpgcr
= imx_readl(gpc_base
+ MXC_SRPG_ARM_SRPGCR
) &
156 empgc0
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
) &
158 empgc1
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
) &
165 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
167 case WAIT_UNCLOCKED_POWER_OFF
:
169 plat_lpc
|= MXC_CORTEXA8_PLAT_LPC_DSM
170 | MXC_CORTEXA8_PLAT_LPC_DBG_DSM
;
171 if (mode
== WAIT_UNCLOCKED_POWER_OFF
) {
172 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
173 ccm_clpcr
&= ~MXC_CCM_CLPCR_VSTBY
;
174 ccm_clpcr
&= ~MXC_CCM_CLPCR_SBYOS
;
177 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
178 ccm_clpcr
|= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET
;
179 ccm_clpcr
|= MXC_CCM_CLPCR_VSTBY
;
180 ccm_clpcr
|= MXC_CCM_CLPCR_SBYOS
;
183 arm_srpgcr
|= MXC_SRPGCR_PCR
;
186 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
189 printk(KERN_WARNING
"UNKNOWN cpu power mode: %d\n", mode
);
193 imx_writel(plat_lpc
, cortex_base
+ MXC_CORTEXA8_PLAT_LPC
);
194 imx_writel(ccm_clpcr
, ccm_base
+ MXC_CCM_CLPCR
);
195 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_ARM_SRPGCR
);
196 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_NEON_SRPGCR
);
199 empgc0
|= MXC_SRPGCR_PCR
;
200 empgc1
|= MXC_SRPGCR_PCR
;
202 imx_writel(empgc0
, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
203 imx_writel(empgc1
, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
207 static int mx5_suspend_enter(suspend_state_t state
)
211 mx5_cpu_lp_set(STOP_POWER_OFF
);
213 case PM_SUSPEND_STANDBY
:
214 /* DEFAULT_IDLE_STATE already configured */
220 if (state
== PM_SUSPEND_MEM
) {
221 local_flush_tlb_all();
224 /*clear the EMPGC0/1 bits */
225 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
226 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
228 if (imx5_suspend_in_ocram_fn
)
229 imx5_suspend_in_ocram_fn(suspend_ocram_base
);
237 /* return registers to default idle state */
238 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
242 static int mx5_pm_valid(suspend_state_t state
)
244 return (state
> PM_SUSPEND_ON
&& state
<= PM_SUSPEND_MAX
);
247 static const struct platform_suspend_ops mx5_suspend_ops
= {
248 .valid
= mx5_pm_valid
,
249 .enter
= mx5_suspend_enter
,
252 static inline int imx5_cpu_do_idle(void)
254 int ret
= tzic_enable_wake();
262 static void imx5_pm_idle(void)
267 static int __init
imx_suspend_alloc_ocram(
269 void __iomem
**virt_out
,
270 phys_addr_t
*phys_out
)
272 struct device_node
*node
;
273 struct platform_device
*pdev
;
274 struct gen_pool
*ocram_pool
;
275 unsigned long ocram_base
;
280 /* Copied from imx6: TODO factorize */
281 node
= of_find_compatible_node(NULL
, NULL
, "mmio-sram");
283 pr_warn("%s: failed to find ocram node!\n", __func__
);
287 pdev
= of_find_device_by_node(node
);
289 pr_warn("%s: failed to find ocram device!\n", __func__
);
294 ocram_pool
= gen_pool_get(&pdev
->dev
, NULL
);
296 pr_warn("%s: ocram pool unavailable!\n", __func__
);
301 ocram_base
= gen_pool_alloc(ocram_pool
, size
);
303 pr_warn("%s: unable to alloc ocram!\n", __func__
);
308 phys
= gen_pool_virt_to_phys(ocram_pool
, ocram_base
);
309 virt
= __arm_ioremap_exec(phys
, size
, false);
316 put_device(&pdev
->dev
);
323 static int __init
imx5_suspend_init(const struct imx5_pm_data
*soc_data
)
325 struct imx5_cpu_suspend_info
*suspend_info
;
327 /* Need this to avoid compile error due to const typeof in fncpy.h */
328 void (*suspend_asm
)(void __iomem
*) = soc_data
->suspend_asm
;
333 if (!soc_data
->suspend_asm_sz
|| !*soc_data
->suspend_asm_sz
)
336 ret
= imx_suspend_alloc_ocram(
337 *soc_data
->suspend_asm_sz
+ sizeof(*suspend_info
),
338 &suspend_ocram_base
, NULL
);
342 suspend_info
= suspend_ocram_base
;
344 suspend_info
->io_count
= soc_data
->suspend_io_count
;
345 memcpy(suspend_info
->io_state
, soc_data
->suspend_io_config
,
346 sizeof(*suspend_info
->io_state
) * soc_data
->suspend_io_count
);
348 suspend_info
->m4if_base
= ioremap(soc_data
->m4if_addr
, SZ_16K
);
349 if (!suspend_info
->m4if_base
) {
351 goto failed_map_m4if
;
354 suspend_info
->iomuxc_base
= ioremap(soc_data
->iomuxc_addr
, SZ_16K
);
355 if (!suspend_info
->iomuxc_base
) {
357 goto failed_map_iomuxc
;
360 imx5_suspend_in_ocram_fn
= fncpy(
361 suspend_ocram_base
+ sizeof(*suspend_info
),
363 *soc_data
->suspend_asm_sz
);
368 iounmap(suspend_info
->m4if_base
);
374 static int __init
imx5_pm_common_init(const struct imx5_pm_data
*data
)
377 struct clk
*gpc_dvfs_clk
= clk_get(NULL
, "gpc_dvfs");
379 if (IS_ERR(gpc_dvfs_clk
))
380 return PTR_ERR(gpc_dvfs_clk
);
382 ret
= clk_prepare_enable(gpc_dvfs_clk
);
386 arm_pm_idle
= imx5_pm_idle
;
388 ccm_base
= ioremap(data
->ccm_addr
, SZ_16K
);
389 cortex_base
= ioremap(data
->cortex_addr
, SZ_16K
);
390 gpc_base
= ioremap(data
->gpc_addr
, SZ_16K
);
391 WARN_ON(!ccm_base
|| !cortex_base
|| !gpc_base
);
393 /* Set the registers to the default cpu idle state. */
394 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
396 ret
= imx5_cpuidle_init();
398 pr_warn("%s: cpuidle init failed %d\n", __func__
, ret
);
400 ret
= imx5_suspend_init(data
);
402 pr_warn("%s: No DDR LPM support with suspend %d!\n",
405 suspend_set_ops(&mx5_suspend_ops
);
410 void __init
imx51_pm_init(void)
412 if (IS_ENABLED(CONFIG_SOC_IMX51
))
413 imx5_pm_common_init(&imx51_pm_data
);
416 void __init
imx53_pm_init(void)
418 if (IS_ENABLED(CONFIG_SOC_IMX53
))
419 imx5_pm_common_init(&imx53_pm_data
);