18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/suspend.h> 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 178c2ecf20Sopenharmony_ci#include <asm/fncpy.h> 188c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "common.h" 228c2ecf20Sopenharmony_ci#include "cpuidle.h" 238c2ecf20Sopenharmony_ci#include "hardware.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR 0x54 268c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR_LPM_OFFSET 0 278c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR_LPM_MASK 0x3 288c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9 298c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR_VSTBY (0x1 << 8) 308c2ecf20Sopenharmony_ci#define MXC_CCM_CLPCR_SBYOS (0x1 << 6) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC 0xc 338c2ecf20Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0) 348c2ecf20Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MXC_SRPG_NEON_SRPGCR 0x280 378c2ecf20Sopenharmony_ci#define MXC_SRPG_ARM_SRPGCR 0x2a0 388c2ecf20Sopenharmony_ci#define MXC_SRPG_EMPGC0_SRPGCR 0x2c0 398c2ecf20Sopenharmony_ci#define MXC_SRPG_EMPGC1_SRPGCR 0x2d0 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MXC_SRPGCR_PCR 1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit. 458c2ecf20Sopenharmony_ci * This is also the lowest power state possible without affecting 468c2ecf20Sopenharmony_ci * non-cpu parts of the system. For these reasons, imx5 should default 478c2ecf20Sopenharmony_ci * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also 488c2ecf20Sopenharmony_ci * uses this state and needs to take no action when registers remain confgiured 498c2ecf20Sopenharmony_ci * for this state. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci#define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct imx5_suspend_io_state { 548c2ecf20Sopenharmony_ci u32 offset; 558c2ecf20Sopenharmony_ci u32 clear; 568c2ecf20Sopenharmony_ci u32 set; 578c2ecf20Sopenharmony_ci u32 saved_value; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct imx5_pm_data { 618c2ecf20Sopenharmony_ci phys_addr_t ccm_addr; 628c2ecf20Sopenharmony_ci phys_addr_t cortex_addr; 638c2ecf20Sopenharmony_ci phys_addr_t gpc_addr; 648c2ecf20Sopenharmony_ci phys_addr_t m4if_addr; 658c2ecf20Sopenharmony_ci phys_addr_t iomuxc_addr; 668c2ecf20Sopenharmony_ci void (*suspend_asm)(void __iomem *ocram_vbase); 678c2ecf20Sopenharmony_ci const u32 *suspend_asm_sz; 688c2ecf20Sopenharmony_ci const struct imx5_suspend_io_state *suspend_io_config; 698c2ecf20Sopenharmony_ci int suspend_io_count; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic const struct imx5_suspend_io_state imx53_suspend_io_config[] = { 738c2ecf20Sopenharmony_ci#define MX53_DSE_HIGHZ_MASK (0x7 << 19) 748c2ecf20Sopenharmony_ci {.offset = 0x584, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM0 */ 758c2ecf20Sopenharmony_ci {.offset = 0x594, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM1 */ 768c2ecf20Sopenharmony_ci {.offset = 0x560, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM2 */ 778c2ecf20Sopenharmony_ci {.offset = 0x554, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM3 */ 788c2ecf20Sopenharmony_ci {.offset = 0x574, .clear = MX53_DSE_HIGHZ_MASK}, /* CAS */ 798c2ecf20Sopenharmony_ci {.offset = 0x588, .clear = MX53_DSE_HIGHZ_MASK}, /* RAS */ 808c2ecf20Sopenharmony_ci {.offset = 0x578, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_0 */ 818c2ecf20Sopenharmony_ci {.offset = 0x570, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_1 */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci {.offset = 0x580, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT0 */ 848c2ecf20Sopenharmony_ci {.offset = 0x564, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT1 */ 858c2ecf20Sopenharmony_ci {.offset = 0x57c, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS0 */ 868c2ecf20Sopenharmony_ci {.offset = 0x590, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS1 */ 878c2ecf20Sopenharmony_ci {.offset = 0x568, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS2 */ 888c2ecf20Sopenharmony_ci {.offset = 0x558, .clear = MX53_DSE_HIGHZ_MASK}, /* SDSQ3 */ 898c2ecf20Sopenharmony_ci {.offset = 0x6f0, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_ADDS */ 908c2ecf20Sopenharmony_ci {.offset = 0x718, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_BODS */ 918c2ecf20Sopenharmony_ci {.offset = 0x71c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B1DS */ 928c2ecf20Sopenharmony_ci {.offset = 0x728, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B2DS */ 938c2ecf20Sopenharmony_ci {.offset = 0x72c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B3DS */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Controls the CKE signal which is required to leave self refresh */ 968c2ecf20Sopenharmony_ci {.offset = 0x720, .clear = MX53_DSE_HIGHZ_MASK, .set = 1 << 19}, /* CTLDS */ 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct imx5_pm_data imx51_pm_data __initconst = { 1008c2ecf20Sopenharmony_ci .ccm_addr = 0x73fd4000, 1018c2ecf20Sopenharmony_ci .cortex_addr = 0x83fa0000, 1028c2ecf20Sopenharmony_ci .gpc_addr = 0x73fd8000, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct imx5_pm_data imx53_pm_data __initconst = { 1068c2ecf20Sopenharmony_ci .ccm_addr = 0x53fd4000, 1078c2ecf20Sopenharmony_ci .cortex_addr = 0x63fa0000, 1088c2ecf20Sopenharmony_ci .gpc_addr = 0x53fd8000, 1098c2ecf20Sopenharmony_ci .m4if_addr = 0x63fd8000, 1108c2ecf20Sopenharmony_ci .iomuxc_addr = 0x53fa8000, 1118c2ecf20Sopenharmony_ci .suspend_asm = &imx53_suspend, 1128c2ecf20Sopenharmony_ci .suspend_asm_sz = &imx53_suspend_sz, 1138c2ecf20Sopenharmony_ci .suspend_io_config = imx53_suspend_io_config, 1148c2ecf20Sopenharmony_ci .suspend_io_count = ARRAY_SIZE(imx53_suspend_io_config), 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * This structure is for passing necessary data for low level ocram 1218c2ecf20Sopenharmony_ci * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct 1228c2ecf20Sopenharmony_ci * definition is changed, the offset definition in that file 1238c2ecf20Sopenharmony_ci * must be also changed accordingly otherwise, the suspend to ocram 1248c2ecf20Sopenharmony_ci * function will be broken! 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistruct imx5_cpu_suspend_info { 1278c2ecf20Sopenharmony_ci void __iomem *m4if_base; 1288c2ecf20Sopenharmony_ci void __iomem *iomuxc_base; 1298c2ecf20Sopenharmony_ci u32 io_count; 1308c2ecf20Sopenharmony_ci struct imx5_suspend_io_state io_state[MX5_MAX_SUSPEND_IOSTATE]; 1318c2ecf20Sopenharmony_ci} __aligned(8); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void __iomem *ccm_base; 1348c2ecf20Sopenharmony_cistatic void __iomem *cortex_base; 1358c2ecf20Sopenharmony_cistatic void __iomem *gpc_base; 1368c2ecf20Sopenharmony_cistatic void __iomem *suspend_ocram_base; 1378c2ecf20Sopenharmony_cistatic void (*imx5_suspend_in_ocram_fn)(void __iomem *ocram_vbase); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * set cpu low power mode before WFI instruction. This function is called 1418c2ecf20Sopenharmony_ci * mx5 because it can be used for mx51, and mx53. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u32 plat_lpc, arm_srpgcr, ccm_clpcr; 1468c2ecf20Sopenharmony_ci u32 empgc0, empgc1; 1478c2ecf20Sopenharmony_ci int stop_mode = 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* always allow platform to issue a deep sleep mode request */ 1508c2ecf20Sopenharmony_ci plat_lpc = imx_readl(cortex_base + MXC_CORTEXA8_PLAT_LPC) & 1518c2ecf20Sopenharmony_ci ~(MXC_CORTEXA8_PLAT_LPC_DSM); 1528c2ecf20Sopenharmony_ci ccm_clpcr = imx_readl(ccm_base + MXC_CCM_CLPCR) & 1538c2ecf20Sopenharmony_ci ~(MXC_CCM_CLPCR_LPM_MASK); 1548c2ecf20Sopenharmony_ci arm_srpgcr = imx_readl(gpc_base + MXC_SRPG_ARM_SRPGCR) & 1558c2ecf20Sopenharmony_ci ~(MXC_SRPGCR_PCR); 1568c2ecf20Sopenharmony_ci empgc0 = imx_readl(gpc_base + MXC_SRPG_EMPGC0_SRPGCR) & 1578c2ecf20Sopenharmony_ci ~(MXC_SRPGCR_PCR); 1588c2ecf20Sopenharmony_ci empgc1 = imx_readl(gpc_base + MXC_SRPG_EMPGC1_SRPGCR) & 1598c2ecf20Sopenharmony_ci ~(MXC_SRPGCR_PCR); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci switch (mode) { 1628c2ecf20Sopenharmony_ci case WAIT_CLOCKED: 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci case WAIT_UNCLOCKED: 1658c2ecf20Sopenharmony_ci ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case WAIT_UNCLOCKED_POWER_OFF: 1688c2ecf20Sopenharmony_ci case STOP_POWER_OFF: 1698c2ecf20Sopenharmony_ci plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM 1708c2ecf20Sopenharmony_ci | MXC_CORTEXA8_PLAT_LPC_DBG_DSM; 1718c2ecf20Sopenharmony_ci if (mode == WAIT_UNCLOCKED_POWER_OFF) { 1728c2ecf20Sopenharmony_ci ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; 1738c2ecf20Sopenharmony_ci ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; 1748c2ecf20Sopenharmony_ci ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; 1758c2ecf20Sopenharmony_ci stop_mode = 0; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; 1788c2ecf20Sopenharmony_ci ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; 1798c2ecf20Sopenharmony_ci ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; 1808c2ecf20Sopenharmony_ci ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; 1818c2ecf20Sopenharmony_ci stop_mode = 1; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci arm_srpgcr |= MXC_SRPGCR_PCR; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case STOP_POWER_ON: 1868c2ecf20Sopenharmony_ci ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci imx_writel(plat_lpc, cortex_base + MXC_CORTEXA8_PLAT_LPC); 1948c2ecf20Sopenharmony_ci imx_writel(ccm_clpcr, ccm_base + MXC_CCM_CLPCR); 1958c2ecf20Sopenharmony_ci imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_ARM_SRPGCR); 1968c2ecf20Sopenharmony_ci imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_NEON_SRPGCR); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (stop_mode) { 1998c2ecf20Sopenharmony_ci empgc0 |= MXC_SRPGCR_PCR; 2008c2ecf20Sopenharmony_ci empgc1 |= MXC_SRPGCR_PCR; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci imx_writel(empgc0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); 2038c2ecf20Sopenharmony_ci imx_writel(empgc1, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int mx5_suspend_enter(suspend_state_t state) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci switch (state) { 2108c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 2118c2ecf20Sopenharmony_ci mx5_cpu_lp_set(STOP_POWER_OFF); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2148c2ecf20Sopenharmony_ci /* DEFAULT_IDLE_STATE already configured */ 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci default: 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (state == PM_SUSPEND_MEM) { 2218c2ecf20Sopenharmony_ci local_flush_tlb_all(); 2228c2ecf20Sopenharmony_ci flush_cache_all(); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /*clear the EMPGC0/1 bits */ 2258c2ecf20Sopenharmony_ci imx_writel(0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); 2268c2ecf20Sopenharmony_ci imx_writel(0, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (imx5_suspend_in_ocram_fn) 2298c2ecf20Sopenharmony_ci imx5_suspend_in_ocram_fn(suspend_ocram_base); 2308c2ecf20Sopenharmony_ci else 2318c2ecf20Sopenharmony_ci cpu_do_idle(); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci cpu_do_idle(); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* return registers to default idle state */ 2388c2ecf20Sopenharmony_ci mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int mx5_pm_valid(suspend_state_t state) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops mx5_suspend_ops = { 2488c2ecf20Sopenharmony_ci .valid = mx5_pm_valid, 2498c2ecf20Sopenharmony_ci .enter = mx5_suspend_enter, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline int imx5_cpu_do_idle(void) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int ret = tzic_enable_wake(); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (likely(!ret)) 2578c2ecf20Sopenharmony_ci cpu_do_idle(); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void imx5_pm_idle(void) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci imx5_cpu_do_idle(); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int __init imx_suspend_alloc_ocram( 2688c2ecf20Sopenharmony_ci size_t size, 2698c2ecf20Sopenharmony_ci void __iomem **virt_out, 2708c2ecf20Sopenharmony_ci phys_addr_t *phys_out) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct device_node *node; 2738c2ecf20Sopenharmony_ci struct platform_device *pdev; 2748c2ecf20Sopenharmony_ci struct gen_pool *ocram_pool; 2758c2ecf20Sopenharmony_ci unsigned long ocram_base; 2768c2ecf20Sopenharmony_ci void __iomem *virt; 2778c2ecf20Sopenharmony_ci phys_addr_t phys; 2788c2ecf20Sopenharmony_ci int ret = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Copied from imx6: TODO factorize */ 2818c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "mmio-sram"); 2828c2ecf20Sopenharmony_ci if (!node) { 2838c2ecf20Sopenharmony_ci pr_warn("%s: failed to find ocram node!\n", __func__); 2848c2ecf20Sopenharmony_ci return -ENODEV; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(node); 2888c2ecf20Sopenharmony_ci if (!pdev) { 2898c2ecf20Sopenharmony_ci pr_warn("%s: failed to find ocram device!\n", __func__); 2908c2ecf20Sopenharmony_ci ret = -ENODEV; 2918c2ecf20Sopenharmony_ci goto put_node; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ocram_pool = gen_pool_get(&pdev->dev, NULL); 2958c2ecf20Sopenharmony_ci if (!ocram_pool) { 2968c2ecf20Sopenharmony_ci pr_warn("%s: ocram pool unavailable!\n", __func__); 2978c2ecf20Sopenharmony_ci ret = -ENODEV; 2988c2ecf20Sopenharmony_ci goto put_device; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ocram_base = gen_pool_alloc(ocram_pool, size); 3028c2ecf20Sopenharmony_ci if (!ocram_base) { 3038c2ecf20Sopenharmony_ci pr_warn("%s: unable to alloc ocram!\n", __func__); 3048c2ecf20Sopenharmony_ci ret = -ENOMEM; 3058c2ecf20Sopenharmony_ci goto put_device; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci phys = gen_pool_virt_to_phys(ocram_pool, ocram_base); 3098c2ecf20Sopenharmony_ci virt = __arm_ioremap_exec(phys, size, false); 3108c2ecf20Sopenharmony_ci if (phys_out) 3118c2ecf20Sopenharmony_ci *phys_out = phys; 3128c2ecf20Sopenharmony_ci if (virt_out) 3138c2ecf20Sopenharmony_ci *virt_out = virt; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ciput_device: 3168c2ecf20Sopenharmony_ci put_device(&pdev->dev); 3178c2ecf20Sopenharmony_ciput_node: 3188c2ecf20Sopenharmony_ci of_node_put(node); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int __init imx5_suspend_init(const struct imx5_pm_data *soc_data) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct imx5_cpu_suspend_info *suspend_info; 3268c2ecf20Sopenharmony_ci int ret; 3278c2ecf20Sopenharmony_ci /* Need this to avoid compile error due to const typeof in fncpy.h */ 3288c2ecf20Sopenharmony_ci void (*suspend_asm)(void __iomem *) = soc_data->suspend_asm; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!suspend_asm) 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!soc_data->suspend_asm_sz || !*soc_data->suspend_asm_sz) 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = imx_suspend_alloc_ocram( 3378c2ecf20Sopenharmony_ci *soc_data->suspend_asm_sz + sizeof(*suspend_info), 3388c2ecf20Sopenharmony_ci &suspend_ocram_base, NULL); 3398c2ecf20Sopenharmony_ci if (ret) 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci suspend_info = suspend_ocram_base; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci suspend_info->io_count = soc_data->suspend_io_count; 3458c2ecf20Sopenharmony_ci memcpy(suspend_info->io_state, soc_data->suspend_io_config, 3468c2ecf20Sopenharmony_ci sizeof(*suspend_info->io_state) * soc_data->suspend_io_count); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci suspend_info->m4if_base = ioremap(soc_data->m4if_addr, SZ_16K); 3498c2ecf20Sopenharmony_ci if (!suspend_info->m4if_base) { 3508c2ecf20Sopenharmony_ci ret = -ENOMEM; 3518c2ecf20Sopenharmony_ci goto failed_map_m4if; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci suspend_info->iomuxc_base = ioremap(soc_data->iomuxc_addr, SZ_16K); 3558c2ecf20Sopenharmony_ci if (!suspend_info->iomuxc_base) { 3568c2ecf20Sopenharmony_ci ret = -ENOMEM; 3578c2ecf20Sopenharmony_ci goto failed_map_iomuxc; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci imx5_suspend_in_ocram_fn = fncpy( 3618c2ecf20Sopenharmony_ci suspend_ocram_base + sizeof(*suspend_info), 3628c2ecf20Sopenharmony_ci suspend_asm, 3638c2ecf20Sopenharmony_ci *soc_data->suspend_asm_sz); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cifailed_map_iomuxc: 3688c2ecf20Sopenharmony_ci iounmap(suspend_info->m4if_base); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cifailed_map_m4if: 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int __init imx5_pm_common_init(const struct imx5_pm_data *data) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci int ret; 3778c2ecf20Sopenharmony_ci struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (IS_ERR(gpc_dvfs_clk)) 3808c2ecf20Sopenharmony_ci return PTR_ERR(gpc_dvfs_clk); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = clk_prepare_enable(gpc_dvfs_clk); 3838c2ecf20Sopenharmony_ci if (ret) 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci arm_pm_idle = imx5_pm_idle; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ccm_base = ioremap(data->ccm_addr, SZ_16K); 3898c2ecf20Sopenharmony_ci cortex_base = ioremap(data->cortex_addr, SZ_16K); 3908c2ecf20Sopenharmony_ci gpc_base = ioremap(data->gpc_addr, SZ_16K); 3918c2ecf20Sopenharmony_ci WARN_ON(!ccm_base || !cortex_base || !gpc_base); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Set the registers to the default cpu idle state. */ 3948c2ecf20Sopenharmony_ci mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = imx5_cpuidle_init(); 3978c2ecf20Sopenharmony_ci if (ret) 3988c2ecf20Sopenharmony_ci pr_warn("%s: cpuidle init failed %d\n", __func__, ret); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ret = imx5_suspend_init(data); 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci pr_warn("%s: No DDR LPM support with suspend %d!\n", 4038c2ecf20Sopenharmony_ci __func__, ret); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci suspend_set_ops(&mx5_suspend_ops); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_civoid __init imx51_pm_init(void) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SOC_IMX51)) 4138c2ecf20Sopenharmony_ci imx5_pm_common_init(&imx51_pm_data); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_civoid __init imx53_pm_init(void) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SOC_IMX53)) 4198c2ecf20Sopenharmony_ci imx5_pm_common_init(&imx53_pm_data); 4208c2ecf20Sopenharmony_ci} 421