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>
15 #include <linux/platform_device.h>
17 #include <asm/cacheflush.h>
18 #include <asm/fncpy.h>
19 #include <asm/system_misc.h>
20 #include <asm/tlbflush.h>
26 #define MXC_CCM_CLPCR 0x54
27 #define MXC_CCM_CLPCR_LPM_OFFSET 0
28 #define MXC_CCM_CLPCR_LPM_MASK 0x3
29 #define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9
30 #define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
31 #define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
33 #define MXC_CORTEXA8_PLAT_LPC 0xc
34 #define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0)
35 #define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1)
37 #define MXC_SRPG_NEON_SRPGCR 0x280
38 #define MXC_SRPG_ARM_SRPGCR 0x2a0
39 #define MXC_SRPG_EMPGC0_SRPGCR 0x2c0
40 #define MXC_SRPG_EMPGC1_SRPGCR 0x2d0
42 #define MXC_SRPGCR_PCR 1
45 * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
46 * This is also the lowest power state possible without affecting
47 * non-cpu parts of the system. For these reasons, imx5 should default
48 * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also
49 * uses this state and needs to take no action when registers remain configured
52 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
54 struct imx5_suspend_io_state
{
63 phys_addr_t cortex_addr
;
65 phys_addr_t m4if_addr
;
66 phys_addr_t iomuxc_addr
;
67 void (*suspend_asm
)(void __iomem
*ocram_vbase
);
68 const u32
*suspend_asm_sz
;
69 const struct imx5_suspend_io_state
*suspend_io_config
;
73 static const struct imx5_suspend_io_state imx53_suspend_io_config
[] = {
74 #define MX53_DSE_HIGHZ_MASK (0x7 << 19)
75 {.offset
= 0x584, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM0 */
76 {.offset
= 0x594, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM1 */
77 {.offset
= 0x560, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM2 */
78 {.offset
= 0x554, .clear
= MX53_DSE_HIGHZ_MASK
}, /* DQM3 */
79 {.offset
= 0x574, .clear
= MX53_DSE_HIGHZ_MASK
}, /* CAS */
80 {.offset
= 0x588, .clear
= MX53_DSE_HIGHZ_MASK
}, /* RAS */
81 {.offset
= 0x578, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_0 */
82 {.offset
= 0x570, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDCLK_1 */
84 {.offset
= 0x580, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT0 */
85 {.offset
= 0x564, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDODT1 */
86 {.offset
= 0x57c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS0 */
87 {.offset
= 0x590, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS1 */
88 {.offset
= 0x568, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDQS2 */
89 {.offset
= 0x558, .clear
= MX53_DSE_HIGHZ_MASK
}, /* SDSQ3 */
90 {.offset
= 0x6f0, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_ADDS */
91 {.offset
= 0x718, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_BODS */
92 {.offset
= 0x71c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B1DS */
93 {.offset
= 0x728, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B2DS */
94 {.offset
= 0x72c, .clear
= MX53_DSE_HIGHZ_MASK
}, /* GRP_B3DS */
96 /* Controls the CKE signal which is required to leave self refresh */
97 {.offset
= 0x720, .clear
= MX53_DSE_HIGHZ_MASK
, .set
= 1 << 19}, /* CTLDS */
100 static const struct imx5_pm_data imx51_pm_data __initconst
= {
101 .ccm_addr
= 0x73fd4000,
102 .cortex_addr
= 0x83fa0000,
103 .gpc_addr
= 0x73fd8000,
106 static const struct imx5_pm_data imx53_pm_data __initconst
= {
107 .ccm_addr
= 0x53fd4000,
108 .cortex_addr
= 0x63fa0000,
109 .gpc_addr
= 0x53fd8000,
110 .m4if_addr
= 0x63fd8000,
111 .iomuxc_addr
= 0x53fa8000,
112 .suspend_asm
= &imx53_suspend
,
113 .suspend_asm_sz
= &imx53_suspend_sz
,
114 .suspend_io_config
= imx53_suspend_io_config
,
115 .suspend_io_count
= ARRAY_SIZE(imx53_suspend_io_config
),
118 #define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config)
121 * This structure is for passing necessary data for low level ocram
122 * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct
123 * definition is changed, the offset definition in that file
124 * must be also changed accordingly otherwise, the suspend to ocram
125 * function will be broken!
127 struct imx5_cpu_suspend_info
{
128 void __iomem
*m4if_base
;
129 void __iomem
*iomuxc_base
;
131 struct imx5_suspend_io_state io_state
[MX5_MAX_SUSPEND_IOSTATE
];
134 static void __iomem
*ccm_base
;
135 static void __iomem
*cortex_base
;
136 static void __iomem
*gpc_base
;
137 static void __iomem
*suspend_ocram_base
;
138 static void (*imx5_suspend_in_ocram_fn
)(void __iomem
*ocram_vbase
);
141 * set cpu low power mode before WFI instruction. This function is called
142 * mx5 because it can be used for mx51, and mx53.
144 static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode
)
146 u32 plat_lpc
, arm_srpgcr
, ccm_clpcr
;
150 /* always allow platform to issue a deep sleep mode request */
151 plat_lpc
= imx_readl(cortex_base
+ MXC_CORTEXA8_PLAT_LPC
) &
152 ~(MXC_CORTEXA8_PLAT_LPC_DSM
);
153 ccm_clpcr
= imx_readl(ccm_base
+ MXC_CCM_CLPCR
) &
154 ~(MXC_CCM_CLPCR_LPM_MASK
);
155 arm_srpgcr
= imx_readl(gpc_base
+ MXC_SRPG_ARM_SRPGCR
) &
157 empgc0
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
) &
159 empgc1
= imx_readl(gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
) &
166 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
168 case WAIT_UNCLOCKED_POWER_OFF
:
170 plat_lpc
|= MXC_CORTEXA8_PLAT_LPC_DSM
171 | MXC_CORTEXA8_PLAT_LPC_DBG_DSM
;
172 if (mode
== WAIT_UNCLOCKED_POWER_OFF
) {
173 ccm_clpcr
|= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET
;
174 ccm_clpcr
&= ~MXC_CCM_CLPCR_VSTBY
;
175 ccm_clpcr
&= ~MXC_CCM_CLPCR_SBYOS
;
178 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
179 ccm_clpcr
|= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET
;
180 ccm_clpcr
|= MXC_CCM_CLPCR_VSTBY
;
181 ccm_clpcr
|= MXC_CCM_CLPCR_SBYOS
;
184 arm_srpgcr
|= MXC_SRPGCR_PCR
;
187 ccm_clpcr
|= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET
;
190 printk(KERN_WARNING
"UNKNOWN cpu power mode: %d\n", mode
);
194 imx_writel(plat_lpc
, cortex_base
+ MXC_CORTEXA8_PLAT_LPC
);
195 imx_writel(ccm_clpcr
, ccm_base
+ MXC_CCM_CLPCR
);
196 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_ARM_SRPGCR
);
197 imx_writel(arm_srpgcr
, gpc_base
+ MXC_SRPG_NEON_SRPGCR
);
200 empgc0
|= MXC_SRPGCR_PCR
;
201 empgc1
|= MXC_SRPGCR_PCR
;
203 imx_writel(empgc0
, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
204 imx_writel(empgc1
, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
208 static int mx5_suspend_enter(suspend_state_t state
)
212 mx5_cpu_lp_set(STOP_POWER_OFF
);
214 case PM_SUSPEND_STANDBY
:
215 /* DEFAULT_IDLE_STATE already configured */
221 if (state
== PM_SUSPEND_MEM
) {
222 local_flush_tlb_all();
225 /*clear the EMPGC0/1 bits */
226 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC0_SRPGCR
);
227 imx_writel(0, gpc_base
+ MXC_SRPG_EMPGC1_SRPGCR
);
229 if (imx5_suspend_in_ocram_fn
)
230 imx5_suspend_in_ocram_fn(suspend_ocram_base
);
238 /* return registers to default idle state */
239 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
243 static int mx5_pm_valid(suspend_state_t state
)
245 return (state
> PM_SUSPEND_ON
&& state
<= PM_SUSPEND_MAX
);
248 static const struct platform_suspend_ops mx5_suspend_ops
= {
249 .valid
= mx5_pm_valid
,
250 .enter
= mx5_suspend_enter
,
253 static inline int imx5_cpu_do_idle(void)
255 int ret
= tzic_enable_wake();
263 static void imx5_pm_idle(void)
268 static int __init
imx_suspend_alloc_ocram(
270 void __iomem
**virt_out
,
271 phys_addr_t
*phys_out
)
273 struct device_node
*node
;
274 struct platform_device
*pdev
;
275 struct gen_pool
*ocram_pool
;
276 unsigned long ocram_base
;
281 /* Copied from imx6: TODO factorize */
282 node
= of_find_compatible_node(NULL
, NULL
, "mmio-sram");
284 pr_warn("%s: failed to find ocram node!\n", __func__
);
288 pdev
= of_find_device_by_node(node
);
290 pr_warn("%s: failed to find ocram device!\n", __func__
);
295 ocram_pool
= gen_pool_get(&pdev
->dev
, NULL
);
297 pr_warn("%s: ocram pool unavailable!\n", __func__
);
302 ocram_base
= gen_pool_alloc(ocram_pool
, size
);
304 pr_warn("%s: unable to alloc ocram!\n", __func__
);
309 phys
= gen_pool_virt_to_phys(ocram_pool
, ocram_base
);
310 virt
= __arm_ioremap_exec(phys
, size
, false);
317 put_device(&pdev
->dev
);
324 static int __init
imx5_suspend_init(const struct imx5_pm_data
*soc_data
)
326 struct imx5_cpu_suspend_info
*suspend_info
;
328 /* Need this to avoid compile error due to const typeof in fncpy.h */
329 void (*suspend_asm
)(void __iomem
*) = soc_data
->suspend_asm
;
334 if (!soc_data
->suspend_asm_sz
|| !*soc_data
->suspend_asm_sz
)
337 ret
= imx_suspend_alloc_ocram(
338 *soc_data
->suspend_asm_sz
+ sizeof(*suspend_info
),
339 &suspend_ocram_base
, NULL
);
343 suspend_info
= suspend_ocram_base
;
345 suspend_info
->io_count
= soc_data
->suspend_io_count
;
346 memcpy(suspend_info
->io_state
, soc_data
->suspend_io_config
,
347 sizeof(*suspend_info
->io_state
) * soc_data
->suspend_io_count
);
349 suspend_info
->m4if_base
= ioremap(soc_data
->m4if_addr
, SZ_16K
);
350 if (!suspend_info
->m4if_base
) {
352 goto failed_map_m4if
;
355 suspend_info
->iomuxc_base
= ioremap(soc_data
->iomuxc_addr
, SZ_16K
);
356 if (!suspend_info
->iomuxc_base
) {
358 goto failed_map_iomuxc
;
361 imx5_suspend_in_ocram_fn
= fncpy(
362 suspend_ocram_base
+ sizeof(*suspend_info
),
364 *soc_data
->suspend_asm_sz
);
369 iounmap(suspend_info
->m4if_base
);
375 static int __init
imx5_pm_common_init(const struct imx5_pm_data
*data
)
378 struct clk
*gpc_dvfs_clk
= clk_get(NULL
, "gpc_dvfs");
380 if (IS_ERR(gpc_dvfs_clk
))
381 return PTR_ERR(gpc_dvfs_clk
);
383 ret
= clk_prepare_enable(gpc_dvfs_clk
);
387 arm_pm_idle
= imx5_pm_idle
;
389 ccm_base
= ioremap(data
->ccm_addr
, SZ_16K
);
390 cortex_base
= ioremap(data
->cortex_addr
, SZ_16K
);
391 gpc_base
= ioremap(data
->gpc_addr
, SZ_16K
);
392 WARN_ON(!ccm_base
|| !cortex_base
|| !gpc_base
);
394 /* Set the registers to the default cpu idle state. */
395 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE
);
397 ret
= imx5_cpuidle_init();
399 pr_warn("%s: cpuidle init failed %d\n", __func__
, ret
);
401 ret
= imx5_suspend_init(data
);
403 pr_warn("%s: No DDR LPM support with suspend %d!\n",
406 suspend_set_ops(&mx5_suspend_ops
);
411 void __init
imx51_pm_init(void)
413 if (IS_ENABLED(CONFIG_SOC_IMX51
))
414 imx5_pm_common_init(&imx51_pm_data
);
417 void __init
imx53_pm_init(void)
419 if (IS_ENABLED(CONFIG_SOC_IMX53
))
420 imx5_pm_common_init(&imx53_pm_data
);